SlideShare ist ein Scribd-Unternehmen logo
1 von 27
Downloaden Sie, um offline zu lesen
1
1
Groovy Plugins
Why you should be developing
Atlassian plugins using Groovy

Dr Paul King, Director, ASERT




                                 2
                                     2
What is Groovy?
 “Groovy is like a super version of Java. It
  can leverage Java's enterprise capabilities
  but also has cool productivity features like
  closures, DSL support, builders and dynamic typing.”
  Groovy	
  =	
  Java	
  –	
  boiler	
  plate	
  code
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  optional	
  dynamic	
  typing
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  closures
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  domain	
  specific	
  languages
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  builders
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  meta-­‐programming
                                                                                                 3
                                                                                                 3
What is Groovy?




 Now free




                  4
                  4
What is Groovy?
                                         What alternative JVM language are you using or intending to use




                                                                                                                            http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes




                                                       http://www.leonardoborges.com/writings




                                                http://it-republik.de/jaxenter/quickvote/results/1/poll/44
                                                (translated using http://babelfish.yahoo.com)




                                                                                                                                                     Source: http://www.micropoll.com/akira/mpresult/501697-116746


                                                                                                      http://www.java.net
 Source: http://www.grailspodcast.com/
                                                                                                                                                                                                                     5
                                                                                                                                                                                                                     5
Reason: Language Features
• Closures
                                 • Productivity
• Runtime metaprogramming
                                 • Clarity
• Compile-time metaprogramming
                                 • Maintainability
• Grape modules
                                 • Quality
• Builders
                                 • Fun
• DSL friendly



                                                     6
                                                         6
Reason: Testing
• Support for Testing DSLs and     • Productivity
  BDD style tests
                                   • Clarity
• Built-in assert, power asserts
                                   • Maintainability
• Built-in testing
                                   • Quality
• Built-in mocks
                                   • Fun
• Metaprogramming eases testing
  pain points                      • Shareability


                                                       7
                                                           7
Myth: Dynamic typing == No IDE support
• Completion through inference
• Code analysis
• Seamless debugging
• Seamless refactoring
• DSL completion




                                          8
                                              8
Myth: Scripting == Non-professional
• Analysis tools
• Coverage tools
• Testing support




                                       9
                                           9
Java                                                                                                       Groovy
import	
  java.util.List;
import	
  java.util.ArrayList;

class	
  Erase	
  {
	
  	
  	
  	
  private	
  List	
  removeLongerThan(List	
  strings,	
  int	
  length)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  List	
  result	
  =	
  new	
  ArrayList();
	
  	
  	
  	
  	
  	
  	
  	
  for	
  (int	
  i	
  =	
  0;	
  i	
  <	
  strings.size();	
  i++)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  String	
  s	
  =	
  (String)	
  strings.get(i);
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (s.length()	
  <=	
  length)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  result.add(s);
                                                                                                           names	
  =	
  ["Ted",	
  "Fred",	
  "Jed",	
  "Ned"]
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }                                                          println	
  names
	
  	
  	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  return	
  result;                                                          shortNames	
  =	
  names.findAll{	
  it.size()	
  <=	
  3	
  }
	
  	
  	
  	
  }
	
  	
  	
  	
  public	
  static	
  void	
  main(String[]	
  args)	
  {                                    println	
  shortNames.size()
	
  	
  	
  	
  	
  	
  	
  	
  List	
  names	
  =	
  new	
  ArrayList();
	
  	
  	
  	
  	
  	
  	
  	
  names.add("Ted");	
  names.add("Fred");
                                                                                                           shortNames.each{	
  println	
  it	
  }
	
  	
  	
  	
  	
  	
  	
  	
  names.add("Jed");	
  names.add("Ned");
	
  	
  	
  	
  	
  	
  	
  	
  System.out.println(names);
	
  	
  	
  	
  	
  	
  	
  	
  Erase	
  e	
  =	
  new	
  Erase();
	
  	
  	
  	
  	
  	
  	
  	
  List	
  shortNames	
  =	
  e.removeLongerThan(names,	
  3);
	
  	
  	
  	
  	
  	
  	
  	
  System.out.println(shortNames.size());
	
  	
  	
  	
  	
  	
  	
  	
  for	
  (int	
  i	
  =	
  0;	
  i	
  <	
  shortNames.size();	
  i++)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  String	
  s	
  =	
  (String)	
  shortNames.get(i);
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  System.out.println(s);
	
  	
  	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  }
}




                                                                                                                                                                            10
                                                                                                                                                                             10
Java                                                                                                                                 Groovy
import	
  org.w3c.dom.Document;
import	
  org.w3c.dom.NodeList;
import	
  org.w3c.dom.Node;
import	
  org.xml.sax.SAXException;

import	
  javax.xml.parsers.DocumentBuilderFactory;
import	
  javax.xml.parsers.DocumentBuilder;
import	
  javax.xml.parsers.ParserConfigurationException;
import	
  java.io.File;
import	
  java.io.IOException;                                                                                                       def	
  p	
  =	
  new	
  XmlParser()
public	
  class	
  FindYearsJava	
  {                                                                                                def	
  records	
  =	
  p.parse("records.xml")
	
  	
  	
  	
  public	
  static	
  void	
  main(String[]	
  args)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  DocumentBuilderFactory	
  builderFactory	
  =	
  DocumentBuilderFactory.newInstance();
                                                                                                                                     records.car.each	
  {
	
  	
  	
  	
  	
  	
  	
  	
  try	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  DocumentBuilder	
  builder	
  =	
  builderFactory.newDocumentBuilder();
                                                                                                                                     	
  	
  	
  	
  println	
  "year	
  =	
  ${it.@year}"
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Document	
  document	
  =	
  builder.parse(new	
  File("records.xml"));              }
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  NodeList	
  list	
  =	
  document.getElementsByTagName("car");
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  for	
  (int	
  i	
  =	
  0;	
  i	
  <	
  list.getLength();	
  i++)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Node	
  n	
  =	
  list.item(i);
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  Node	
  year	
  =	
  n.getAttributes().getNamedItem("year");
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  System.out.println("year	
  =	
  "	
  +	
  year.getTextContent());
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (ParserConfigurationException	
  e)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  e.printStackTrace();
	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (SAXException	
  e)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  e.printStackTrace();
	
  	
  	
  	
  	
  	
  	
  	
  }	
  catch	
  (IOException	
  e)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  e.printStackTrace();
	
  	
  	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  }
}




                                                                                                                                                                                             11
                                                                                                                                                                                              11
Java                                                                                                                                                                                  Groovy
public	
  final	
  class	
  Punter	
  {                                                              	
  	
  	
  	
  //	
  ...
	
  	
  	
  	
  private	
  final	
  String	
  first;                                                 	
  	
  	
  	
  @Override
	
  	
  	
  	
  private	
  final	
  String	
  last;                                                  	
  	
  	
  	
  public	
  boolean	
  equals(Object	
  obj)	
  {
                                                                                                     	
  	
  	
  	
  	
  	
  	
  	
  if	
  (this	
  ==	
  obj)
	
  	
  	
  	
  public	
  String	
  getFirst()	
  {                                                  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  	
  	
  	
  	
  return	
  first;                                                     	
  	
  	
  	
  	
  	
  	
  	
  if	
  (obj	
  ==	
  null)
	
  	
  	
  	
  }                                                                                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
                                                                                                     	
  	
  	
  	
  	
  	
  	
  	
  if	
  (getClass()	
  !=	
  obj.getClass())                    @Immutable	
  class	
  Punter	
  {
	
  	
  	
  	
  public	
  String	
  getLast()	
  {                                                   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
	
  	
  	
  	
  	
  	
  	
  	
  return	
  last;                                                      	
  	
  	
  	
  	
  	
  	
  	
  Punter	
  other	
  =	
  (Punter)	
  obj;                      	
  	
  	
  	
  String	
  first,	
  last
	
  	
  	
  	
  }                                                                                    	
  	
  	
  	
  	
  	
  	
  	
  if	
  (first	
  ==	
  null)	
  {
                                                                                                     	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (other.first	
  !=	
  null)
                                                                                                                                                                                                   }
	
  	
  	
  	
  @Override                                                                            	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
	
  	
  	
  	
  public	
  int	
  hashCode()	
  {                                                     	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  if	
  (!first.equals(other.first))
	
  	
  	
  	
  	
  	
  	
  	
  final	
  int	
  prime	
  =	
  31;                                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
	
  	
  	
  	
  	
  	
  	
  	
  int	
  result	
  =	
  1;                                             	
  	
  	
  	
  	
  	
  	
  	
  if	
  (last	
  ==	
  null)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  result	
  =	
  prime	
  *	
  result	
  +	
  ((first	
  ==	
  null)   	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  if	
  (other.last	
  !=	
  null)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ?	
  0	
  :	
  first.hashCode());                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
	
  	
  	
  	
  	
  	
  	
  	
  result	
  =	
  prime	
  *	
  result	
  +	
  ((last	
  ==	
  null)    	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  if	
  (!last.equals(other.last))
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ?	
  0	
  :	
  last.hashCode());                     	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  false;
	
  	
  	
  	
  	
  	
  	
  	
  return	
  result;                                                    	
  	
  	
  	
  	
  	
  	
  	
  return	
  true;
	
  	
  	
  	
  }                                                                                    	
  	
  	
  	
  }

	
  	
  	
  	
  public	
  Punter(String	
  first,	
  String	
  last)	
  {                            	
  	
  	
  	
  @Override
	
  	
  	
  	
  	
  	
  	
  	
  this.first	
  =	
  first;                                            	
  	
  	
  	
  public	
  String	
  toString()	
  {
	
  	
  	
  	
  	
  	
  	
  	
  this.last	
  =	
  last;                                              	
  	
  	
  	
  	
  	
  	
  	
  return	
  "Punter(first:"	
  +	
  first
	
  	
  	
  	
  }                                                                                    	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  +	
  ",	
  last:"	
  +	
  last	
  +	
  ")";
	
  	
  	
  	
  //	
  ...                                                                            	
  	
  	
  	
  }

                                                                                                     }




                                                                                                                                                                                                                                              12
                                                                                                                                                                                                                                               12
Java                                                            Groovy
public class CustomException extends RuntimeException {
    public CustomException() {
        super();                                                @InheritConstructors
    }
                                                                class CustomException
    public CustomException(String message) {                    extends RuntimeException { }
        super(message);
    }

    public CustomException(String message, Throwable cause) {
        super(message, cause);
    }

    public CustomException(Throwable cause) {
        super(cause);
    }
}




                                                                                               13
                                                                                                13
Groovy
@Grab('com.google.collections:google-­‐collections:1.0')
import	
  com.google.common.collect.HashBiMap                                                   @Grab('org.gcontracts:gcontracts:1.0.2')                           Groovy 1.8+
                                                                                                import	
  org.gcontracts.annotations.*
HashBiMap	
  fruit	
  =
	
  	
  [grape:'purple',	
  lemon:'yellow',	
  lime:'green']
                                                                                                @Invariant({	
  first	
  !=	
  null	
  &&	
  last	
  !=	
  null	
  })
assert	
  fruit.lemon	
  ==	
  'yellow'                                                         class	
  Person	
  {
assert	
  fruit.inverse().yellow	
  ==	
  'lemon'                                               	
  	
  	
  String	
  first,	
  last

                                                                                                	
  	
  	
  @Requires({	
  delimiter	
  in	
  ['.',	
  ',',	
  '	
  ']	
  })
          @Grab('org.codehaus.gpars:gpars:0.10')                                                	
  	
  	
  @Ensures({	
  result	
  ==	
  first+delimiter+last	
  })
          import	
  groovyx.gpars.agent.Agent                                                   	
  	
  	
  String	
  getName(String	
  delimiter)	
  {
                                                                                                	
  	
  	
  	
  	
  	
  first	
  +	
  delimiter	
  +	
  last
          withPool(5)	
  {
                                                                                                	
  	
  	
  }
          	
  	
  	
  	
  def	
  nums	
  =	
  1..100000
                                                                                                }
          	
  	
  	
  	
  println	
  nums.parallel.
          	
  	
  	
  	
  	
  	
  	
  	
  map{	
  it	
  **	
  2	
  }.
                                                                                                new	
  Person(first:	
  'John',	
  last:	
  'Smith').getName('.')
          	
  	
  	
  	
  	
  	
  	
  	
  filter{	
  it	
  %	
  7	
  ==	
  it	
  %	
  5	
  }.
          	
  	
  	
  	
  	
  	
  	
  	
  filter{	
  it	
  %	
  3	
  ==	
  0	
  }.
          	
  	
  	
  	
  	
  	
  	
  	
  reduce{	
  a,	
  b	
  -­‐>	
  a	
  +	
  b	
  }
          }
                                           Groovy and Gpars both OSGi compliant
                                                                                                                                                                               14
                                                                                                                                                                                14
Plugin Tutorial: World of WarCraft...
• http://confluence.atlassian.com/display/CONFDEV/
  WoW+Macro+explanation




                                                    15
                                                     15
...Plugin Tutorial: World of WarCraft...
            • Normal instructions for gmaven:
              http://gmaven.codehaus.org/
           	
  	
  ...
           	
  	
  <plugin>
           	
  	
  	
  	
  	
  <groupId>org.codehaus.gmaven</groupId>
           	
  	
  	
  	
  	
  <artifactId>gmaven-­‐plugin</artifactId>
           	
  	
  	
  	
  	
  <version>1.2</version>
           	
  	
  	
  	
  	
  <configuration>...</configuration>
           	
  	
  	
  	
  	
  <executions>...</executions>
           	
  	
  	
  	
  	
  <dependencies>...</dependencies>
           	
  	
  </plugin>
           	
  	
  ...




                                                                          16
                                                                           16
...Plugin Tutorial: World of WarCraft...
package	
  com.atlassian.confluence.plugins.wowplugin;                                                                                        ...
                                                                                                                                              	
  	
  	
  	
  public	
  String	
  getName()	
  {
import	
  java.io.Serializable;                                                                                                               	
  	
  	
  	
  	
  	
  	
  	
  return	
  name;
import	
  java.util.Arrays;                                                                                                                   	
  	
  	
  	
  }
import	
  java.util.List;
                                                                                                                                              	
  	
  	
  	
  public	
  String	
  getSpec()	
  {
/**                                                                                                                                           	
  	
  	
  	
  	
  	
  	
  	
  return	
  spec;
*	
  Simple	
  data	
  holder	
  for	
  basic	
  toon	
  information                                                                          	
  	
  	
  	
  }
*/
public	
  final	
  class	
  Toon	
  implements	
  Comparable,	
  Serializable                                                                 	
  	
  	
  	
  public	
  int	
  getGearScore()	
  {
{                                                                                                                                             	
  	
  	
  	
  	
  	
  	
  	
  return	
  gearScore;
	
  	
  	
  	
  private	
  static	
  final	
  String[]	
  CLASSES	
  =	
  {                                                                   	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Warrior",
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Paladin",                                                                                    	
  	
  	
  	
  public	
  List	
  getRecommendedRaids()	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Hunter",                                                                                     	
  	
  	
  	
  	
  	
  	
  	
  return	
  recommendedRaids;
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Rogue",                                                                                      	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Priest",
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Death	
  Knight",                                                                            	
  	
  	
  	
  public	
  String	
  getClassName()	
  {
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Shaman",                                                                                     	
  	
  	
  	
  	
  	
  	
  	
  return	
  className;
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Mage",                                                                                       	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Warlock",
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Unknown",	
  //	
  There	
  is	
  no	
  class	
  with	
  ID	
  10.	
  Weird.                 	
  	
  	
  	
  public	
  int	
  compareTo(Object	
  o)
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "Druid"                                                                                       	
  	
  	
  	
  {
	
  	
  	
  	
  };                                                                                                                            	
  	
  	
  	
  	
  	
  	
  	
  Toon	
  otherToon	
  =	
  (Toon)	
  o;

	
  	
  	
  	
  private	
  final	
  String	
  name;                                                                                           	
  	
  	
  	
  	
  	
  	
  	
  if	
  (otherToon.gearScore	
  -­‐	
  gearScore	
  !=	
  0)
	
  	
  	
  	
  private	
  final	
  String	
  spec;                                                                                           	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  otherToon.gearScore	
  -­‐	
  gearScore;
	
  	
  	
  	
  private	
  final	
  int	
  gearScore;
	
  	
  	
  	
  private	
  final	
  List	
  recommendedRaids;                                                                                 	
  	
  	
  	
  	
  	
  	
  	
  return	
  name.compareTo(otherToon.name);
	
  	
  	
  	
  private	
  final	
  String	
  className;                                                                                      	
  	
  	
  	
  }

	
  	
  	
  	
  public	
  Toon(String	
  name,	
  int	
  classId,	
  String	
  spec,	
  int	
  gearScore,	
  String...	
  recommendedRaids)   	
  	
  	
  	
  private	
  String	
  toClassName(int	
  classIndex)
	
  	
  	
  	
  {                                                                                                                             	
  	
  	
  	
  {
	
  	
  	
  	
  	
  	
  	
  	
  this.className	
  =	
  toClassName(classId	
  -­‐	
  1);                                                      	
  	
  	
  	
  	
  	
  	
  	
  if	
  (classIndex	
  <	
  0	
  ||	
  classIndex	
  >=	
  CLASSES.length)
	
  	
  	
  	
  	
  	
  	
  	
  this.name	
  =	
  name;                                                                                       	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  "Unknown:	
  "	
  +	
  classIndex	
  +	
  1;
	
  	
  	
  	
  	
  	
  	
  	
  this.spec	
  =	
  spec;                                                                                       	
  	
  	
  	
  	
  	
  	
  	
  else
	
  	
  	
  	
  	
  	
  	
  	
  this.gearScore	
  =	
  gearScore;                                                                             	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  return	
  CLASSES[classIndex];
	
  	
  	
  	
  	
  	
  	
  	
  this.recommendedRaids	
  =	
  Arrays.asList(recommendedRaids);                                                	
  	
  	
  	
  }
	
  	
  	
  	
  }                                                                                                                             }
...



                                                                                                                                                                                                                                                         17
                                                                                                                                                                                                                                                          17
...Plugin Tutorial: World of WarCraft...
package	
  com.atlassian.confluence.plugins.gwowplugin

class	
  Toon	
  implements	
  Serializable	
  {
	
  	
  	
  	
  private	
  static	
  final	
  String[]	
  CLASSES	
  =	
  [
	
  	
  	
  	
  	
  	
  	
  	
  "Warrior",	
  "Paladin",	
  "Hunter",	
  "Rogue",	
  "Priest",
	
  	
  	
  	
  	
  	
  	
  	
  "Death	
  Knight",	
  "Shaman",	
  "Mage",	
  "Warlock",	
  "Unknown",	
  "Druid"]                                83 -> 17

	
  	
  	
  	
  String	
  name
	
  	
  	
  	
  int	
  classId
	
  	
  	
  	
  String	
  spec
	
  	
  	
  	
  int	
  gearScore
	
  	
  	
  	
  def	
  recommendedRaids

	
  	
  	
  	
  String	
  getClassName()	
  {
	
  	
  	
  	
  	
  	
  	
  	
  classId	
  in	
  0..<CLASSES.length	
  ?	
  CLASSES[classId	
  -­‐	
  1]	
  :	
  "Unknown:	
  "	
  +	
  classId
	
  	
  	
  	
  }
}



                                                                                                                                                       18
                                                                                                                                                             18
...Plugin Tutorial: World of WarCraft...
package com.atlassian.confluence.plugins.wowplugin;                   ...                                                                                       ...
                                                                            public boolean isInline() { return false; }                                                 try {
import   com.atlassian.cache.Cache;                                                                                                                                         url = String.format("http://xml.wow-heroes.com/xml-guild.php?z=%s&r=%s&g=%s",
import   com.atlassian.cache.CacheManager;                                  public boolean hasBody() { return false; }                                                              URLEncoder.encode(zone, "UTF-8"),
import   com.atlassian.confluence.util.http.HttpResponse;                                                                                                                           URLEncoder.encode(realmName, "UTF-8"),
import   com.atlassian.confluence.util.http.HttpRetrievalService;           public RenderMode getBodyRenderMode() {                                                                 URLEncoder.encode(guildName, "UTF-8"));
import   com.atlassian.renderer.RenderContext;                                  return RenderMode.NO_RENDER;                                                            } catch (UnsupportedEncodingException e) {
import   com.atlassian.renderer.v2.RenderMode;                              }                                                                                               throw new MacroException(e.getMessage(), e);
import   com.atlassian.renderer.v2.SubRenderer;                                                                                                                         }
import   com.atlassian.renderer.v2.macro.BaseMacro;                         public String execute(Map map, String s, RenderContext renderContext) throws MacroException {
import   com.atlassian.renderer.v2.macro.MacroException;                        String guildName = (String) map.get("guild");                                           Cache cache = cacheManager.getCache(this.getClass().getName() + ".toons");
import   org.dom4j.Document;                                                    String realmName = (String) map.get("realm");
import   org.dom4j.DocumentException;                                           String zone = (String) map.get("zone");                                                 if (cache.get(url) != null)
import   org.dom4j.Element;                                                     if (zone == null) zone = "us";                                                              return (List<Toon>) cache.get(url);
import   org.dom4j.io.SAXReader;
                                                                                StringBuilder out = new StringBuilder("||Name||Class||Gear Score");
                                                                                                                                                                       try {
                                                                                for (int i = 0; i < SHORT_RAIDS.length; i++) {
import   java.io.IOException;                                                                                                                                              List<Toon> toons = retrieveAndParseFromWowArmory(url);
                                                                                    out.append("||").append(SHORT_RAIDS[i].replace('/', 'n'));
import   java.io.InputStream;                                                                                                                                              cache.put(url, toons);
                                                                                }
import   java.io.UnsupportedEncodingException;                                                                                                                             return toons;
                                                                                out.append("||n");
import   java.net.URLEncoder;                                                                                                                                          }
import   java.util.*;                                                                                                                                                  catch (IOException e) {
                                                                              List<Toon> toons = retrieveToons(guildName, realmName, zone);
                                                                                                                                                                           throw new MacroException("Unable to retrieve information for guild: " + guildName + ", " + e.toString());
/**                                                                           for (Toon toon : toons) {                                                                }
 * Inserts a table of a guild's roster of 80s ranked by gear level, with recommended raid instances. The data for                                                      catch (DocumentException e) {
 * the macro is grabbed from http://wow-heroes.com. Results are cached for $DEFAULT_CACHE_LIFETIME to reduce
                                                                                  out.append("| ");                                                                        throw new MacroException("Unable to parse information for guild: " + guildName + ", " + e.toString());
 * load on the server.                                                            try {                                                                                }
 * <p/>                                                                                                                                                            }
                                                                                       String url = String.format("http://xml.wow-heroes.com/index.php?zone=%s&server=%s&name=%s",
 * Usage: {guild-gear|realm=Nagrand|guild=A New Beginning|zone=us}                              URLEncoder.encode(zone, "UTF-8"),
 * <p/>                                                                                         URLEncoder.encode(realmName, "UTF-8"),                             private List<Toon> retrieveAndParseFromWowArmory(String url) throws IOException, DocumentException {
 * Problems:                                                                                    URLEncoder.encode(toon.getName(), "UTF-8"));                           List<Toon> toons = new ArrayList<Toon>();
 * <p/>                                                                                out.append("["); out.append(toon.getName());                                    HttpResponse response = httpRetrievalService.get(url);
 * * wow-heroes reports your main spec, but whatever gear you logged out in. So if you out.append("|"); out.append(url); out.append("]");
                                                                                        logged out in off-spec gear
 * your number will be wrong                                                      }                                                                                    InputStream responseStream = response.getResponse();
 * * gear score != ability. l2play nub.                                           catch (UnsupportedEncodingException e) {                                             try {
 */                                                                                    out.append(toon.getName());                                                         SAXReader reader = new SAXReader();
public class GuildGearMacro extends BaseMacro {                                   }                                                                                        Document doc = reader.read(responseStream);
    private HttpRetrievalService httpRetrievalService;                                                                                                                     List toonsXml = doc.selectNodes("//character");
    private SubRenderer subRenderer;                                              out.append(" | ");                                                                       for (Object o : toonsXml) {
    private CacheManager cacheManager;                                            out.append(toon.getClassName());                                                             Element element = (Element) o;
                                                                                  out.append(" (");                                                                            toons.add(new Toon(element.attributeValue("name"), Integer.parseInt(element.attributeValue("classId")),
    private static final String[] RAIDS = {                                       out.append(toon.getSpec());                                                                          element.attributeValue("specName"),
             "Heroics",                                                           out.append(")");                                                                                     Integer.parseInt(element.attributeValue("score")), element.attributeValue("suggest").split(";")));
             "Naxxramas 10", // and OS10                                          out.append("|");                                                                         }
             "Naxxramas 25", // and OS25/EoE10                                    out.append(toon.getGearScore());
             "Ulduar 10", // and EoE25                                            boolean found = false;                                                                   Collections.sort(toons);
             "Onyxia 10",                                                                                                                                              }
             "Ulduar 25", // and ToTCr10                                          for (String raid : RAIDS) {                                                          finally {
             "Onyxia 25",                                                              if (toon.getRecommendedRaids().contains(raid)) {                                    responseStream.close();
             "Trial of the Crusader 25",                                                   out.append("|(!)");                                                         }
             "Icecrown Citadel 10"                                                         found = true;                                                               return toons;
    };                                                                                 } else {                                                                    }
                                                                                           out.append("|").append(found ? "(x)" : "(/)");
    private static final String[] SHORT_RAIDS = {                                      }                                                                           public void setHttpRetrievalService(HttpRetrievalService httpRetrievalService) {
             "H",                                                                 }                                                                                    this.httpRetrievalService = httpRetrievalService;
             "Naxx10/OS10",                                                       out.append("|n");                                                               }
             "Naxx25/OS25/EoE10",                                             }
             "Uld10/EoE25",                                                                                                                                        public void setSubRenderer(SubRenderer subRenderer) {
             "Ony10",                                                         return subRenderer.render(out.toString(), renderContext);                                this.subRenderer = subRenderer;
             "Uld25/TotCr10",                                             }                                                                                        }
             "Ony25",
             "TotCr25",                                                   private List<Toon> retrieveToons(String guildName, String realmName, String zone)        public void setCacheManager(CacheManager cacheManager) {
             "IC"                                                                 throws MacroException {                                                              this.cacheManager = cacheManager;
    };                                                                        String url = null;                                                                   }
    ...                                                               ...                                                                                      }

                                                                                                                                                                                                                                                                                 19
                                                                                                                                                                                                                                                                                       19
...Plugin Tutorial: World of WarCraft...
package	
  com.atlassian.confluence.plugins.gwowplugin                                                                                          ...
                                                                                                                                                	
  	
  	
  	
  toons.each	
  {	
  toon	
  -­‐>
import	
  com.atlassian.cache.CacheManager                                                                                                      	
  	
  	
  	
  	
  	
  def	
  url	
  =	
  "http://xml.wow-­‐heroes.com/index.php?zone=${enc	
  zone}&server=${enc	
  map.realm}&name=${enc	
  toon.name}"
import	
  com.atlassian.confluence.util.http.HttpRetrievalService                                                                               	
  	
  	
  	
  	
  	
  out.append("|	
  [${toon.name}|${url}]	
  |	
  $toon.className	
  ($toon.spec)|	
  $toon.gearScore")
import	
  com.atlassian.renderer.RenderContext                                                                                                  	
  	
  	
  	
  	
  	
  boolean	
  found	
  =	
  false
import	
  com.atlassian.renderer.v2.RenderMode                                                                                                  	
  	
  	
  	
  	
  	
  RAIDS.each	
  {	
  raid	
  -­‐>
import	
  com.atlassian.renderer.v2.SubRenderer                                                                                                 	
  	
  	
  	
  	
  	
  	
  	
  if	
  (raid	
  in	
  toon.recommendedRaids)	
  {
import	
  com.atlassian.renderer.v2.macro.BaseMacro                                                                                             	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  out.append("|(!)")
import	
  com.atlassian.renderer.v2.macro.MacroException                                                                                        	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  found	
  =	
  true
                                                                                                                                                	
  	
  	
  	
  	
  	
  	
  	
  }	
  else	
  {
/**                                                                                                                                             	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  out.append("|").append(found	
  ?	
  "(x)"	
  :	
  "(/)")
	
  *	
  Inserts	
  a	
  table	
  of	
  a	
  guild's	
  roster	
  of	
  80s	
  ranked	
  by	
  gear	
  level,	
  with	
  recommended	
  raid    	
  	
  	
  	
  	
  	
  	
  	
  }
	
  *	
  instances.	
  The	
  data	
  for	
  the	
  macro	
  is	
  grabbed	
  from	
  http://wow-­‐heroes.com.	
  Results	
  are                	
  	
  	
  	
  	
  	
  }                                                                                                                     200 -> 90
	
  *	
  cached	
  for	
  $DEFAULT_CACHE_LIFETIME	
  to	
  reduce	
  load	
  on	
  the	
  server.                                               	
  	
  	
  	
  	
  	
  out.append("|n")
	
  *	
  <p/>                                                                                                                                   	
  	
  	
  	
  }
	
  *	
  Usage:	
  {guild-­‐gear:realm=Nagrand|guild=A	
  New	
  Beginning|zone=us}                                                             	
  	
  	
  	
  subRenderer.render(out.toString(),	
  renderContext)
	
  */                                                                                                                                          	
  	
  }
class	
  GuildGearMacro	
  extends	
  BaseMacro	
  {
	
  	
  HttpRetrievalService	
  httpRetrievalService                                                                                            	
  	
  private	
  retrieveToons(String	
  guildName,	
  String	
  realmName,	
  String	
  zone)	
  throws	
  MacroException	
  {
	
  	
  SubRenderer	
  subRenderer                                                                                                              	
  	
  	
  	
  def	
  url	
  =	
  "http://xml.wow-­‐heroes.com/xml-­‐guild.php?z=${enc	
  zone}&r=${enc	
  realmName}&g=${enc	
  guildName}"
	
  	
  CacheManager	
  cacheManager                                                                                                            	
  	
  	
  	
  def	
  cache	
  =	
  cacheManager.getCache(this.class.name	
  +	
  ".toons")
                                                                                                                                                	
  	
  	
  	
  if	
  (!cache.get(url))	
  cache.put(url,	
  retrieveAndParseFromWowArmory(url))
	
  	
  private	
  static	
  final	
  String[]	
  RAIDS	
  =	
  [                                                                               	
  	
  	
  	
  return	
  cache.get(url)
	
  	
  	
  	
  	
  	
  	
  	
  "Heroics",	
  "Naxxramas	
  10",	
  "Naxxramas	
  25",	
  "Ulduar	
  10",	
  "Onyxia	
  10",                    	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  "Ulduar	
  25",	
  "Onyxia	
  25",	
  "Trial	
  of	
  the	
  Crusader	
  25",	
  "Icecrown	
  Citadel	
  10"]
	
  	
  private	
  static	
  final	
  String[]	
  SHORT_RAIDS	
  =	
  [                                                                         	
  	
  private	
  retrieveAndParseFromWowArmory(String	
  url)	
  {
	
  	
  	
  	
  	
  	
  	
  	
  "H",	
  "Naxx10/OS10",	
  "Naxx25/OS25/EoE10",	
  "Uld10/EoE25",	
  "Ony10",                                    	
  	
  	
  	
  def	
  toons
	
  	
  	
  	
  	
  	
  	
  	
  "Uld25/TotCr10",	
  "Ony25",	
  "TotCr25",	
  "IC"]                                                             	
  	
  	
  	
  httpRetrievalService.get(url).response.withReader	
  {	
  reader	
  -­‐>
                                                                                                                                                	
  	
  	
  	
  	
  	
  toons	
  =	
  new	
  XmlSlurper().parse(reader).guild.character.collect	
  {
	
  	
  boolean	
  isInline()	
  {	
  false	
  }                                                                                                	
  	
  	
  	
  	
  	
  	
  	
  new	
  Toon(
	
  	
  boolean	
  hasBody()	
  {	
  false	
  }                                                                                                 	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  name:	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  it.@name,
	
  	
  RenderMode	
  getBodyRenderMode()	
  {	
  RenderMode.NO_RENDER	
  }                                                                     	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  classId:	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  it.@classId.toInteger(),
                                                                                                                                                	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  spec:	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  it.@specName,
	
  	
  String	
  execute(Map	
  map,	
  String	
  s,	
  RenderContext	
  renderContext)	
  throws	
  MacroException	
  {                       	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  gearScore:	
  	
  	
  	
  	
  	
  	
  	
  it.@score.toInteger(),
	
  	
  	
  	
  def	
  zone	
  =	
  map.zone	
  ?:	
  "us"                                                                                      	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  recommendedRaids:	
  it.@suggest.toString().split(";"))
	
  	
  	
  	
  def	
  out	
  =	
  new	
  StringBuilder("||Name||Class||Gear	
  Score")                                                         	
  	
  	
  	
  	
  	
  }
	
  	
  	
  	
  SHORT_RAIDS.each	
  {	
  out.append("||").append(it.replace('/',	
  'n'))	
  }                                                 	
  	
  	
  	
  }
	
  	
  	
  	
  out.append("||n")                                                                                                              	
  	
  	
  	
  toons.sort{	
  a,	
  b	
  -­‐>	
  a.gearScore	
  ==	
  b.gearScore	
  ?	
  a.name	
  <=>	
  b.name	
  :	
  a.gearScore	
  <=>	
  b.gearScore	
  }
                                                                                                                                                	
  	
  }
	
  	
  	
  	
  def	
  toons	
  =	
  retrieveToons(map.guild,	
  map.realm,	
  zone)
...                                                                                                                                             	
  	
  def	
  enc(s)	
  {	
  URLEncoder.encode(s,	
  'UTF-­‐8')	
  }
                                                                                                                                                }                                                                                                                                                                   20
                                                                                                                                                                                                                                                                                                                     20
...Plugin Tutorial: World of WarCraft...
  {groovy-wow-item:1624}   {groovy-guild-gear:realm=Kirin Tor|guild=Faceroll Syndicate|zone=us}




                                                                                                  21
                                                                                                   21
...Plugin Tutorial: World of WarCraft...
> atlas-mvn clover2:setup test clover2:aggregate clover2:clover




                                                                  22
                                                                   22
...Plugin Tutorial: World of WarCraft
                                                                                        narrative	
  'segment	
  flown',	
  {
package	
  com.atlassian.confluence.plugins.gwowplugin
                                                                                        	
  	
  	
  	
  as_a	
  'frequent	
  flyer'
                                                                                        	
  	
  	
  	
  i_want	
  'to	
  accrue	
  rewards	
  points	
  for	
  every	
  segment	
  I	
  fly'
class	
  ToonSpec	
  extends	
  spock.lang.Specification	
  {                           	
  	
  	
  	
  so_that	
  'I	
  can	
  receive	
  free	
  flights	
  for	
  my	
  dedication	
  to	
  the	
  airline'
	
  	
  	
  	
  def	
  "successful	
  name	
  of	
  Toon	
  given	
  classId"()	
  {    }

                                                                                        scenario	
  'segment	
  flown',	
  {
	
  	
  	
  	
  	
  	
  	
  	
  given:                                                  	
  	
  	
  	
  given	
  'a	
  frequent	
  flyer	
  with	
  a	
  rewards	
  balance	
  of	
  1500	
  points'
	
  	
  	
  	
  	
  	
  	
  	
  def	
  t	
  =	
  new	
  Toon(classId:	
  thisClassId)   	
  	
  	
  	
  when	
  'that	
  flyer	
  completes	
  a	
  segment	
  worth	
  500	
  points'
                                                                                        	
  	
  	
  	
  then	
  'that	
  flyer	
  has	
  a	
  new	
  rewards	
  balance	
  of	
  2000	
  points'
	
  	
  	
  	
  	
  	
  	
  	
  expect:                                                 }
	
  	
  	
  	
  	
  	
  	
  	
  t.className	
  ==	
  name
                                                                                        scenario	
  'segment	
  flown',	
  {
                                                                                        	
  	
  	
  	
  	
  given	
  'a	
  frequent	
  flyer	
  with	
  a	
  rewards	
  balance	
  of	
  1500	
  points',	
  {
	
  	
  	
  	
  	
  	
  	
  	
  where:                                                  	
  	
  	
  	
  	
  	
  	
  	
  	
  flyer	
  =	
  new	
  FrequentFlyer(1500)
	
  	
  	
  	
  	
  	
  	
  	
  name	
  	
  	
  	
  	
  	
  	
  |	
  	
  thisClassId    	
  	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  "Hunter"	
  	
  	
  |	
  	
  3                          	
  	
  	
  	
  	
  when	
  'that	
  flyer	
  completes	
  a	
  segment	
  worth	
  500	
  points',	
  {
	
  	
  	
  	
  	
  	
  	
  	
  "Rogue"	
  	
  	
  	
  |	
  	
  4                       	
  	
  	
  	
  	
  	
  	
  	
  	
  flyer.fly(new	
  Segment(500))
                                                                                        	
  	
  	
  	
  	
  }
	
  	
  	
  	
  	
  	
  	
  	
  "Priest"	
  	
  	
  |	
  	
  5                          	
  	
  	
  	
  	
  then	
  'that	
  flyer	
  has	
  a	
  new	
  rewards	
  balance	
  of	
  2000	
  points',	
  {
                                                                                        	
  	
  	
  	
  	
  	
  	
  	
  	
  flyer.pointsBalance.shouldBe	
  2000
	
  	
  	
  	
  }                                                                       	
  	
  	
  	
  	
  }
}•                                                                                      	
  }



   •                Testing with Spock                                                   • Or Cucumber, EasyB, JBehave,

                                                                                                                                                                                                                 23
                                                                                                                                                                                                                  23
Scripting on the fly...




                         24
                          24
...Scripting on the fly...




                            25
                             25
...Scripting on the fly




                         26
                          26
27
 27

Weitere ähnliche Inhalte

Was ist angesagt?

KYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlKYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlCoimbra JUG
 
DevNexus 2019: Migrating to Java 11
DevNexus 2019: Migrating to Java 11DevNexus 2019: Migrating to Java 11
DevNexus 2019: Migrating to Java 11DaliaAboSheasha
 
Migrating to Java 11
Migrating to Java 11Migrating to Java 11
Migrating to Java 11Arto Santala
 
Lorraine JUG (1st June, 2010) - Maven
Lorraine JUG (1st June, 2010) - MavenLorraine JUG (1st June, 2010) - Maven
Lorraine JUG (1st June, 2010) - MavenArnaud Héritier
 
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)Mihail Stoynov
 
Java EE 8 Update
Java EE 8 UpdateJava EE 8 Update
Java EE 8 UpdateRyan Cuprak
 
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"Daniel Bryant
 
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012Sam Brannen
 
Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點William Yeh
 
Beyond TDD: Enabling Your Team to Continuously Deliver Software
Beyond TDD: Enabling Your Team to Continuously Deliver SoftwareBeyond TDD: Enabling Your Team to Continuously Deliver Software
Beyond TDD: Enabling Your Team to Continuously Deliver SoftwareChris Weldon
 
Dev Tools State of the Union (Part II) - Atlassian Summit 2010
Dev Tools State of the Union (Part II) - Atlassian Summit 2010Dev Tools State of the Union (Part II) - Atlassian Summit 2010
Dev Tools State of the Union (Part II) - Atlassian Summit 2010Atlassian
 
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDIMigrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDIMario-Leander Reimer
 
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...0xdaryl
 
Java EE 6 Clustering with Glassfish 3.1
Java EE 6 Clustering with Glassfish 3.1 Java EE 6 Clustering with Glassfish 3.1
Java EE 6 Clustering with Glassfish 3.1 Shreedhar Ganapathy
 

Was ist angesagt? (20)

Java EE 8
Java EE 8Java EE 8
Java EE 8
 
KYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under ControlKYSUC - Keep Your Schema Under Control
KYSUC - Keep Your Schema Under Control
 
DevNexus 2019: Migrating to Java 11
DevNexus 2019: Migrating to Java 11DevNexus 2019: Migrating to Java 11
DevNexus 2019: Migrating to Java 11
 
JEE 8, A Big Overview
JEE 8, A Big OverviewJEE 8, A Big Overview
JEE 8, A Big Overview
 
Migrating to Java 11
Migrating to Java 11Migrating to Java 11
Migrating to Java 11
 
Lorraine JUG (1st June, 2010) - Maven
Lorraine JUG (1st June, 2010) - MavenLorraine JUG (1st June, 2010) - Maven
Lorraine JUG (1st June, 2010) - Maven
 
4 maven junit
4 maven junit4 maven junit
4 maven junit
 
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)
Modules in Java? Finally! (OpenJDK 9 Jigsaw, JSR376)
 
Java EE 8 Update
Java EE 8 UpdateJava EE 8 Update
Java EE 8 Update
 
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"
J1 2015 "Debugging Java Apps in Containers: No Heavy Welding Gear Required"
 
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012
Spring 3.1 to 3.2 in a Nutshell - Spring I/O 2012
 
Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點Monitoring 改造計畫:流程觀點
Monitoring 改造計畫:流程觀點
 
Java cro'21 the best tools for java developers in 2021 - hujak
Java cro'21   the best tools for java developers in 2021 - hujakJava cro'21   the best tools for java developers in 2021 - hujak
Java cro'21 the best tools for java developers in 2021 - hujak
 
Javantura Zagreb 2014 - Vaadin - Peter Lehto
Javantura Zagreb 2014 - Vaadin - Peter LehtoJavantura Zagreb 2014 - Vaadin - Peter Lehto
Javantura Zagreb 2014 - Vaadin - Peter Lehto
 
Beyond TDD: Enabling Your Team to Continuously Deliver Software
Beyond TDD: Enabling Your Team to Continuously Deliver SoftwareBeyond TDD: Enabling Your Team to Continuously Deliver Software
Beyond TDD: Enabling Your Team to Continuously Deliver Software
 
Dev Tools State of the Union (Part II) - Atlassian Summit 2010
Dev Tools State of the Union (Part II) - Atlassian Summit 2010Dev Tools State of the Union (Part II) - Atlassian Summit 2010
Dev Tools State of the Union (Part II) - Atlassian Summit 2010
 
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDIMigrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI
Migrating a JSF-Based Web Application from Spring 3 to Java EE 7 and CDI
 
JavaEE 6 tools coverage
JavaEE 6 tools coverageJavaEE 6 tools coverage
JavaEE 6 tools coverage
 
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...
JavaOne 2015 CON7547 "Beyond the Coffee Cup: Leveraging Java Runtime Technolo...
 
Java EE 6 Clustering with Glassfish 3.1
Java EE 6 Clustering with Glassfish 3.1 Java EE 6 Clustering with Glassfish 3.1
Java EE 6 Clustering with Glassfish 3.1
 

Andere mochten auch

Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...
Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...
Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...Atlassian
 
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...Atlassian
 
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...Atlassian
 
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...Atlassian
 
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...Atlassian
 
Dev Tools State of the Union (Part I) - Atlassian Summit 2010
Dev Tools State of the Union (Part I) - Atlassian Summit 2010Dev Tools State of the Union (Part I) - Atlassian Summit 2010
Dev Tools State of the Union (Part I) - Atlassian Summit 2010Atlassian
 
Delivering World-Class Documentation and Support Through Confluence - Atlassi...
Delivering World-Class Documentation and Support Through Confluence - Atlassi...Delivering World-Class Documentation and Support Through Confluence - Atlassi...
Delivering World-Class Documentation and Support Through Confluence - Atlassi...Atlassian
 
Kaizen With GreenHopper: Visualising Agile & Kanban Storywalls
Kaizen With GreenHopper: Visualising Agile & Kanban StorywallsKaizen With GreenHopper: Visualising Agile & Kanban Storywalls
Kaizen With GreenHopper: Visualising Agile & Kanban StorywallsCraig Smith
 
Mastering JIRA Workflow - Atlassian Summit 2010
Mastering JIRA Workflow - Atlassian Summit 2010Mastering JIRA Workflow - Atlassian Summit 2010
Mastering JIRA Workflow - Atlassian Summit 2010Atlassian
 
Automating your processes with JIRA
Automating your processes with JIRAAutomating your processes with JIRA
Automating your processes with JIRAAdaptavist
 
JIRA Performance Testing in Pictures - Edward Bukoski Michael March
JIRA Performance Testing in Pictures - Edward Bukoski Michael MarchJIRA Performance Testing in Pictures - Edward Bukoski Michael March
JIRA Performance Testing in Pictures - Edward Bukoski Michael MarchAtlassian
 
Mantis Bug Tracker & Task Management System
Mantis Bug Tracker & Task Management SystemMantis Bug Tracker & Task Management System
Mantis Bug Tracker & Task Management SystemMindfire Solutions
 
Ctxaug 02 amd atlassian build pipeline
Ctxaug 02   amd atlassian build pipelineCtxaug 02   amd atlassian build pipeline
Ctxaug 02 amd atlassian build pipelinepraecipio
 
Using JIRA Software for Issue Tracking
Using JIRA Software for Issue TrackingUsing JIRA Software for Issue Tracking
Using JIRA Software for Issue TrackingAnjali Rao
 
Introduction To Jira
Introduction To JiraIntroduction To Jira
Introduction To JiraHua Soon Sim
 

Andere mochten auch (17)

Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...
Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...
Auditing Your Build and Release Infrastructure - Atlassian Summit 2010 - Ligh...
 
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...
The Information Radiator: Creating Awesome Wallboards - Atlassian Summit 2010...
 
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...
5 Thing You're Not Doing, 4 Things You Should Stop Doing & 3 Things You Shoul...
 
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...
Labels Magic: Getting Labels to Work for Your Confluence Deployment - Atlassi...
 
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...
Going Agile: Brought to You by the Public Broadcasting System - Atlassian Sum...
 
Dev Tools State of the Union (Part I) - Atlassian Summit 2010
Dev Tools State of the Union (Part I) - Atlassian Summit 2010Dev Tools State of the Union (Part I) - Atlassian Summit 2010
Dev Tools State of the Union (Part I) - Atlassian Summit 2010
 
Jira 4 Demo
Jira 4 DemoJira 4 Demo
Jira 4 Demo
 
Delivering World-Class Documentation and Support Through Confluence - Atlassi...
Delivering World-Class Documentation and Support Through Confluence - Atlassi...Delivering World-Class Documentation and Support Through Confluence - Atlassi...
Delivering World-Class Documentation and Support Through Confluence - Atlassi...
 
Kaizen With GreenHopper: Visualising Agile & Kanban Storywalls
Kaizen With GreenHopper: Visualising Agile & Kanban StorywallsKaizen With GreenHopper: Visualising Agile & Kanban Storywalls
Kaizen With GreenHopper: Visualising Agile & Kanban Storywalls
 
Mastering JIRA Workflow - Atlassian Summit 2010
Mastering JIRA Workflow - Atlassian Summit 2010Mastering JIRA Workflow - Atlassian Summit 2010
Mastering JIRA Workflow - Atlassian Summit 2010
 
Automating your processes with JIRA
Automating your processes with JIRAAutomating your processes with JIRA
Automating your processes with JIRA
 
JIRA Performance Testing in Pictures - Edward Bukoski Michael March
JIRA Performance Testing in Pictures - Edward Bukoski Michael MarchJIRA Performance Testing in Pictures - Edward Bukoski Michael March
JIRA Performance Testing in Pictures - Edward Bukoski Michael March
 
Mantis Bug Tracker & Task Management System
Mantis Bug Tracker & Task Management SystemMantis Bug Tracker & Task Management System
Mantis Bug Tracker & Task Management System
 
Ctxaug 02 amd atlassian build pipeline
Ctxaug 02   amd atlassian build pipelineCtxaug 02   amd atlassian build pipeline
Ctxaug 02 amd atlassian build pipeline
 
Introducing JIRA AGILE
Introducing JIRA AGILEIntroducing JIRA AGILE
Introducing JIRA AGILE
 
Using JIRA Software for Issue Tracking
Using JIRA Software for Issue TrackingUsing JIRA Software for Issue Tracking
Using JIRA Software for Issue Tracking
 
Introduction To Jira
Introduction To JiraIntroduction To Jira
Introduction To Jira
 

Ähnlich wie Building Atlassian Plugins with Groovy - Atlassian Summit 2010 - Lightning Talks

Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneAndres Almiray
 
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentationTestcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentationRichard North
 
Everything as a Code / Александр Тарасов (Одноклассники)
Everything as a Code / Александр Тарасов (Одноклассники)Everything as a Code / Александр Тарасов (Одноклассники)
Everything as a Code / Александр Тарасов (Одноклассники)Ontico
 
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?
Hadoop:  Big Data Stacks validation w/ iTest  How to tame the elephant?Hadoop:  Big Data Stacks validation w/ iTest  How to tame the elephant?
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?Dmitri Shiryaev
 
GOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter SlidesGOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter SlidesAlexandra Masterson
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersKostas Saidis
 
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume Laforge
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume LaforgeGroovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume Laforge
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume LaforgeGuillaume Laforge
 
Testing Ext JS and Sencha Touch
Testing Ext JS and Sencha TouchTesting Ext JS and Sencha Touch
Testing Ext JS and Sencha TouchMats Bryntse
 
"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James NelsonGWTcon
 
What we can expect from Java 9 by Ivan Krylov
What we can expect from Java 9 by Ivan KrylovWhat we can expect from Java 9 by Ivan Krylov
What we can expect from Java 9 by Ivan KrylovJ On The Beach
 
What to expect from Java 9
What to expect from Java 9What to expect from Java 9
What to expect from Java 9Ivan Krylov
 

Ähnlich wie Building Atlassian Plugins with Groovy - Atlassian Summit 2010 - Lightning Talks (20)

Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
Oscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast LaneOscon Java Testing on the Fast Lane
Oscon Java Testing on the Fast Lane
 
Core Java
Core JavaCore Java
Core Java
 
Gradle
GradleGradle
Gradle
 
Building XWiki
Building XWikiBuilding XWiki
Building XWiki
 
Testcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentationTestcontainers - Geekout EE 2017 presentation
Testcontainers - Geekout EE 2017 presentation
 
Everything as a Code / Александр Тарасов (Одноклассники)
Everything as a Code / Александр Тарасов (Одноклассники)Everything as a Code / Александр Тарасов (Одноклассники)
Everything as a Code / Александр Тарасов (Одноклассники)
 
Everything as a code
Everything as a codeEverything as a code
Everything as a code
 
55 New Features in Java 7
55 New Features in Java 755 New Features in Java 7
55 New Features in Java 7
 
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?
Hadoop:  Big Data Stacks validation w/ iTest  How to tame the elephant?Hadoop:  Big Data Stacks validation w/ iTest  How to tame the elephant?
Hadoop: Big Data Stacks validation w/ iTest How to tame the elephant?
 
GOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter SlidesGOTO Night with Charles Nutter Slides
GOTO Night with Charles Nutter Slides
 
An Introduction to Gradle for Java Developers
An Introduction to Gradle for Java DevelopersAn Introduction to Gradle for Java Developers
An Introduction to Gradle for Java Developers
 
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume Laforge
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume LaforgeGroovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume Laforge
Groovy in the Enterprise - Case Studies - TSSJS Prague 2008 - Guillaume Laforge
 
Testing Ext JS and Sencha Touch
Testing Ext JS and Sencha TouchTesting Ext JS and Sencha Touch
Testing Ext JS and Sencha Touch
 
"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson"Xapi-lang For declarative code generation" By James Nelson
"Xapi-lang For declarative code generation" By James Nelson
 
Js tacktalk team dev js testing performance
Js tacktalk team dev js testing performanceJs tacktalk team dev js testing performance
Js tacktalk team dev js testing performance
 
What we can expect from Java 9 by Ivan Krylov
What we can expect from Java 9 by Ivan KrylovWhat we can expect from Java 9 by Ivan Krylov
What we can expect from Java 9 by Ivan Krylov
 
Java 9 preview
Java 9 previewJava 9 preview
Java 9 preview
 
What to expect from Java 9
What to expect from Java 9What to expect from Java 9
What to expect from Java 9
 
Continuous feature-development
Continuous feature-developmentContinuous feature-development
Continuous feature-development
 

Mehr von Atlassian

International Women's Day 2020
International Women's Day 2020International Women's Day 2020
International Women's Day 2020Atlassian
 
10 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 202010 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 2020Atlassian
 
Forge App Showcase
Forge App ShowcaseForge App Showcase
Forge App ShowcaseAtlassian
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UIAtlassian
 
Meet the Forge Runtime
Meet the Forge RuntimeMeet the Forge Runtime
Meet the Forge RuntimeAtlassian
 
Forge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceForge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceAtlassian
 
Take Action with Forge Triggers
Take Action with Forge TriggersTake Action with Forge Triggers
Take Action with Forge TriggersAtlassian
 
Observability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeObservability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeAtlassian
 
Trusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelTrusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelAtlassian
 
Designing Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemDesigning Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemAtlassian
 
Forge: Under the Hood
Forge: Under the HoodForge: Under the Hood
Forge: Under the HoodAtlassian
 
Access to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAccess to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAtlassian
 
Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginAtlassian
 
Tear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingTear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingAtlassian
 
Nailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterNailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterAtlassian
 
Building Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindBuilding Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindAtlassian
 
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Atlassian
 
Beyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsBeyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsAtlassian
 
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamThe Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamAtlassian
 
Building Apps With Enterprise in Mind
Building Apps With Enterprise in MindBuilding Apps With Enterprise in Mind
Building Apps With Enterprise in MindAtlassian
 

Mehr von Atlassian (20)

International Women's Day 2020
International Women's Day 2020International Women's Day 2020
International Women's Day 2020
 
10 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 202010 emerging trends that will unbreak your workplace in 2020
10 emerging trends that will unbreak your workplace in 2020
 
Forge App Showcase
Forge App ShowcaseForge App Showcase
Forge App Showcase
 
Let's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UILet's Build an Editor Macro with Forge UI
Let's Build an Editor Macro with Forge UI
 
Meet the Forge Runtime
Meet the Forge RuntimeMeet the Forge Runtime
Meet the Forge Runtime
 
Forge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User ExperienceForge UI: A New Way to Customize the Atlassian User Experience
Forge UI: A New Way to Customize the Atlassian User Experience
 
Take Action with Forge Triggers
Take Action with Forge TriggersTake Action with Forge Triggers
Take Action with Forge Triggers
 
Observability and Troubleshooting in Forge
Observability and Troubleshooting in ForgeObservability and Troubleshooting in Forge
Observability and Troubleshooting in Forge
 
Trusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy ModelTrusted by Default: The Forge Security & Privacy Model
Trusted by Default: The Forge Security & Privacy Model
 
Designing Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI SystemDesigning Forge UI: A Story of Designing an App UI System
Designing Forge UI: A Story of Designing an App UI System
 
Forge: Under the Hood
Forge: Under the HoodForge: Under the Hood
Forge: Under the Hood
 
Access to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIsAccess to User Activities - Activity Platform APIs
Access to User Activities - Activity Platform APIs
 
Design Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch PluginDesign Your Next App with the Atlassian Vendor Sketch Plugin
Design Your Next App with the Atlassian Vendor Sketch Plugin
 
Tear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the BuildingTear Up Your Roadmap and Get Out of the Building
Tear Up Your Roadmap and Get Out of the Building
 
Nailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that MatterNailing Measurement: a Framework for Measuring Metrics that Matter
Nailing Measurement: a Framework for Measuring Metrics that Matter
 
Building Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in MindBuilding Apps With Color Blind Users in Mind
Building Apps With Color Blind Users in Mind
 
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
 
Beyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced TeamsBeyond Diversity: A Guide to Building Balanced Teams
Beyond Diversity: A Guide to Building Balanced Teams
 
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed TeamThe Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
 
Building Apps With Enterprise in Mind
Building Apps With Enterprise in MindBuilding Apps With Enterprise in Mind
Building Apps With Enterprise in Mind
 

Kürzlich hochgeladen

Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demoHarshalMandlekar2
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 

Kürzlich hochgeladen (20)

Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Sample pptx for embedding into website for demo
Sample pptx for embedding into website for demoSample pptx for embedding into website for demo
Sample pptx for embedding into website for demo
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 

Building Atlassian Plugins with Groovy - Atlassian Summit 2010 - Lightning Talks

  • 1. 1 1
  • 2. Groovy Plugins Why you should be developing Atlassian plugins using Groovy Dr Paul King, Director, ASERT 2 2
  • 3. What is Groovy? “Groovy is like a super version of Java. It can leverage Java's enterprise capabilities but also has cool productivity features like closures, DSL support, builders and dynamic typing.” Groovy  =  Java  –  boiler  plate  code                            +  optional  dynamic  typing                            +  closures                            +  domain  specific  languages                            +  builders                            +  meta-­‐programming 3 3
  • 4. What is Groovy? Now free 4 4
  • 5. What is Groovy? What alternative JVM language are you using or intending to use http://www.jroller.com/scolebourne/entry/devoxx_2008_whiteboard_votes http://www.leonardoborges.com/writings http://it-republik.de/jaxenter/quickvote/results/1/poll/44 (translated using http://babelfish.yahoo.com) Source: http://www.micropoll.com/akira/mpresult/501697-116746 http://www.java.net Source: http://www.grailspodcast.com/ 5 5
  • 6. Reason: Language Features • Closures • Productivity • Runtime metaprogramming • Clarity • Compile-time metaprogramming • Maintainability • Grape modules • Quality • Builders • Fun • DSL friendly 6 6
  • 7. Reason: Testing • Support for Testing DSLs and • Productivity BDD style tests • Clarity • Built-in assert, power asserts • Maintainability • Built-in testing • Quality • Built-in mocks • Fun • Metaprogramming eases testing pain points • Shareability 7 7
  • 8. Myth: Dynamic typing == No IDE support • Completion through inference • Code analysis • Seamless debugging • Seamless refactoring • DSL completion 8 8
  • 9. Myth: Scripting == Non-professional • Analysis tools • Coverage tools • Testing support 9 9
  • 10. Java Groovy import  java.util.List; import  java.util.ArrayList; class  Erase  {        private  List  removeLongerThan(List  strings,  int  length)  {                List  result  =  new  ArrayList();                for  (int  i  =  0;  i  <  strings.size();  i++)  {                        String  s  =  (String)  strings.get(i);                        if  (s.length()  <=  length)  {                                result.add(s); names  =  ["Ted",  "Fred",  "Jed",  "Ned"]                        } println  names                }                return  result; shortNames  =  names.findAll{  it.size()  <=  3  }        }        public  static  void  main(String[]  args)  { println  shortNames.size()                List  names  =  new  ArrayList();                names.add("Ted");  names.add("Fred"); shortNames.each{  println  it  }                names.add("Jed");  names.add("Ned");                System.out.println(names);                Erase  e  =  new  Erase();                List  shortNames  =  e.removeLongerThan(names,  3);                System.out.println(shortNames.size());                for  (int  i  =  0;  i  <  shortNames.size();  i++)  {                        String  s  =  (String)  shortNames.get(i);                        System.out.println(s);                }        } } 10 10
  • 11. Java Groovy import  org.w3c.dom.Document; import  org.w3c.dom.NodeList; import  org.w3c.dom.Node; import  org.xml.sax.SAXException; import  javax.xml.parsers.DocumentBuilderFactory; import  javax.xml.parsers.DocumentBuilder; import  javax.xml.parsers.ParserConfigurationException; import  java.io.File; import  java.io.IOException; def  p  =  new  XmlParser() public  class  FindYearsJava  { def  records  =  p.parse("records.xml")        public  static  void  main(String[]  args)  {                DocumentBuilderFactory  builderFactory  =  DocumentBuilderFactory.newInstance(); records.car.each  {                try  {                        DocumentBuilder  builder  =  builderFactory.newDocumentBuilder();        println  "year  =  ${it.@year}"                        Document  document  =  builder.parse(new  File("records.xml")); }                        NodeList  list  =  document.getElementsByTagName("car");                        for  (int  i  =  0;  i  <  list.getLength();  i++)  {                                Node  n  =  list.item(i);                                Node  year  =  n.getAttributes().getNamedItem("year");                                System.out.println("year  =  "  +  year.getTextContent());                        }                }  catch  (ParserConfigurationException  e)  {                        e.printStackTrace();                }  catch  (SAXException  e)  {                        e.printStackTrace();                }  catch  (IOException  e)  {                        e.printStackTrace();                }        } } 11 11
  • 12. Java Groovy public  final  class  Punter  {        //  ...        private  final  String  first;        @Override        private  final  String  last;        public  boolean  equals(Object  obj)  {                if  (this  ==  obj)        public  String  getFirst()  {                        return  true;                return  first;                if  (obj  ==  null)        }                        return  false;                if  (getClass()  !=  obj.getClass()) @Immutable  class  Punter  {        public  String  getLast()  {                        return  false;                return  last;                Punter  other  =  (Punter)  obj;        String  first,  last        }                if  (first  ==  null)  {                        if  (other.first  !=  null) }        @Override                                return  false;        public  int  hashCode()  {                }  else  if  (!first.equals(other.first))                final  int  prime  =  31;                        return  false;                int  result  =  1;                if  (last  ==  null)  {                result  =  prime  *  result  +  ((first  ==  null)                        if  (other.last  !=  null)                        ?  0  :  first.hashCode());                                return  false;                result  =  prime  *  result  +  ((last  ==  null)                }  else  if  (!last.equals(other.last))                        ?  0  :  last.hashCode());                        return  false;                return  result;                return  true;        }        }        public  Punter(String  first,  String  last)  {        @Override                this.first  =  first;        public  String  toString()  {                this.last  =  last;                return  "Punter(first:"  +  first        }                        +  ",  last:"  +  last  +  ")";        //  ...        } } 12 12
  • 13. Java Groovy public class CustomException extends RuntimeException { public CustomException() { super(); @InheritConstructors } class CustomException public CustomException(String message) { extends RuntimeException { } super(message); } public CustomException(String message, Throwable cause) { super(message, cause); } public CustomException(Throwable cause) { super(cause); } } 13 13
  • 14. Groovy @Grab('com.google.collections:google-­‐collections:1.0') import  com.google.common.collect.HashBiMap @Grab('org.gcontracts:gcontracts:1.0.2') Groovy 1.8+ import  org.gcontracts.annotations.* HashBiMap  fruit  =    [grape:'purple',  lemon:'yellow',  lime:'green'] @Invariant({  first  !=  null  &&  last  !=  null  }) assert  fruit.lemon  ==  'yellow' class  Person  { assert  fruit.inverse().yellow  ==  'lemon'      String  first,  last      @Requires({  delimiter  in  ['.',  ',',  '  ']  }) @Grab('org.codehaus.gpars:gpars:0.10')      @Ensures({  result  ==  first+delimiter+last  }) import  groovyx.gpars.agent.Agent      String  getName(String  delimiter)  {            first  +  delimiter  +  last withPool(5)  {      }        def  nums  =  1..100000 }        println  nums.parallel.                map{  it  **  2  }. new  Person(first:  'John',  last:  'Smith').getName('.')                filter{  it  %  7  ==  it  %  5  }.                filter{  it  %  3  ==  0  }.                reduce{  a,  b  -­‐>  a  +  b  } } Groovy and Gpars both OSGi compliant 14 14
  • 15. Plugin Tutorial: World of WarCraft... • http://confluence.atlassian.com/display/CONFDEV/ WoW+Macro+explanation 15 15
  • 16. ...Plugin Tutorial: World of WarCraft... • Normal instructions for gmaven: http://gmaven.codehaus.org/    ...    <plugin>          <groupId>org.codehaus.gmaven</groupId>          <artifactId>gmaven-­‐plugin</artifactId>          <version>1.2</version>          <configuration>...</configuration>          <executions>...</executions>          <dependencies>...</dependencies>    </plugin>    ... 16 16
  • 17. ...Plugin Tutorial: World of WarCraft... package  com.atlassian.confluence.plugins.wowplugin; ...        public  String  getName()  { import  java.io.Serializable;                return  name; import  java.util.Arrays;        } import  java.util.List;        public  String  getSpec()  { /**                return  spec; *  Simple  data  holder  for  basic  toon  information        } */ public  final  class  Toon  implements  Comparable,  Serializable        public  int  getGearScore()  { {                return  gearScore;        private  static  final  String[]  CLASSES  =  {        }                        "Warrior",                        "Paladin",        public  List  getRecommendedRaids()  {                        "Hunter",                return  recommendedRaids;                        "Rogue",        }                        "Priest",                        "Death  Knight",        public  String  getClassName()  {                        "Shaman",                return  className;                        "Mage",        }                        "Warlock",                        "Unknown",  //  There  is  no  class  with  ID  10.  Weird.        public  int  compareTo(Object  o)                        "Druid"        {        };                Toon  otherToon  =  (Toon)  o;        private  final  String  name;                if  (otherToon.gearScore  -­‐  gearScore  !=  0)        private  final  String  spec;                        return  otherToon.gearScore  -­‐  gearScore;        private  final  int  gearScore;        private  final  List  recommendedRaids;                return  name.compareTo(otherToon.name);        private  final  String  className;        }        public  Toon(String  name,  int  classId,  String  spec,  int  gearScore,  String...  recommendedRaids)        private  String  toClassName(int  classIndex)        {        {                this.className  =  toClassName(classId  -­‐  1);                if  (classIndex  <  0  ||  classIndex  >=  CLASSES.length)                this.name  =  name;                        return  "Unknown:  "  +  classIndex  +  1;                this.spec  =  spec;                else                this.gearScore  =  gearScore;                        return  CLASSES[classIndex];                this.recommendedRaids  =  Arrays.asList(recommendedRaids);        }        } } ... 17 17
  • 18. ...Plugin Tutorial: World of WarCraft... package  com.atlassian.confluence.plugins.gwowplugin class  Toon  implements  Serializable  {        private  static  final  String[]  CLASSES  =  [                "Warrior",  "Paladin",  "Hunter",  "Rogue",  "Priest",                "Death  Knight",  "Shaman",  "Mage",  "Warlock",  "Unknown",  "Druid"] 83 -> 17        String  name        int  classId        String  spec        int  gearScore        def  recommendedRaids        String  getClassName()  {                classId  in  0..<CLASSES.length  ?  CLASSES[classId  -­‐  1]  :  "Unknown:  "  +  classId        } } 18 18
  • 19. ...Plugin Tutorial: World of WarCraft... package com.atlassian.confluence.plugins.wowplugin; ... ... public boolean isInline() { return false; } try { import com.atlassian.cache.Cache; url = String.format("http://xml.wow-heroes.com/xml-guild.php?z=%s&r=%s&g=%s", import com.atlassian.cache.CacheManager; public boolean hasBody() { return false; } URLEncoder.encode(zone, "UTF-8"), import com.atlassian.confluence.util.http.HttpResponse; URLEncoder.encode(realmName, "UTF-8"), import com.atlassian.confluence.util.http.HttpRetrievalService; public RenderMode getBodyRenderMode() { URLEncoder.encode(guildName, "UTF-8")); import com.atlassian.renderer.RenderContext; return RenderMode.NO_RENDER; } catch (UnsupportedEncodingException e) { import com.atlassian.renderer.v2.RenderMode; } throw new MacroException(e.getMessage(), e); import com.atlassian.renderer.v2.SubRenderer; } import com.atlassian.renderer.v2.macro.BaseMacro; public String execute(Map map, String s, RenderContext renderContext) throws MacroException { import com.atlassian.renderer.v2.macro.MacroException; String guildName = (String) map.get("guild"); Cache cache = cacheManager.getCache(this.getClass().getName() + ".toons"); import org.dom4j.Document; String realmName = (String) map.get("realm"); import org.dom4j.DocumentException; String zone = (String) map.get("zone"); if (cache.get(url) != null) import org.dom4j.Element; if (zone == null) zone = "us"; return (List<Toon>) cache.get(url); import org.dom4j.io.SAXReader; StringBuilder out = new StringBuilder("||Name||Class||Gear Score"); try { for (int i = 0; i < SHORT_RAIDS.length; i++) { import java.io.IOException; List<Toon> toons = retrieveAndParseFromWowArmory(url); out.append("||").append(SHORT_RAIDS[i].replace('/', 'n')); import java.io.InputStream; cache.put(url, toons); } import java.io.UnsupportedEncodingException; return toons; out.append("||n"); import java.net.URLEncoder; } import java.util.*; catch (IOException e) { List<Toon> toons = retrieveToons(guildName, realmName, zone); throw new MacroException("Unable to retrieve information for guild: " + guildName + ", " + e.toString()); /** for (Toon toon : toons) { } * Inserts a table of a guild's roster of 80s ranked by gear level, with recommended raid instances. The data for catch (DocumentException e) { * the macro is grabbed from http://wow-heroes.com. Results are cached for $DEFAULT_CACHE_LIFETIME to reduce out.append("| "); throw new MacroException("Unable to parse information for guild: " + guildName + ", " + e.toString()); * load on the server. try { } * <p/> } String url = String.format("http://xml.wow-heroes.com/index.php?zone=%s&server=%s&name=%s", * Usage: {guild-gear|realm=Nagrand|guild=A New Beginning|zone=us} URLEncoder.encode(zone, "UTF-8"), * <p/> URLEncoder.encode(realmName, "UTF-8"), private List<Toon> retrieveAndParseFromWowArmory(String url) throws IOException, DocumentException { * Problems: URLEncoder.encode(toon.getName(), "UTF-8")); List<Toon> toons = new ArrayList<Toon>(); * <p/> out.append("["); out.append(toon.getName()); HttpResponse response = httpRetrievalService.get(url); * * wow-heroes reports your main spec, but whatever gear you logged out in. So if you out.append("|"); out.append(url); out.append("]"); logged out in off-spec gear * your number will be wrong } InputStream responseStream = response.getResponse(); * * gear score != ability. l2play nub. catch (UnsupportedEncodingException e) { try { */ out.append(toon.getName()); SAXReader reader = new SAXReader(); public class GuildGearMacro extends BaseMacro { } Document doc = reader.read(responseStream); private HttpRetrievalService httpRetrievalService; List toonsXml = doc.selectNodes("//character"); private SubRenderer subRenderer; out.append(" | "); for (Object o : toonsXml) { private CacheManager cacheManager; out.append(toon.getClassName()); Element element = (Element) o; out.append(" ("); toons.add(new Toon(element.attributeValue("name"), Integer.parseInt(element.attributeValue("classId")), private static final String[] RAIDS = { out.append(toon.getSpec()); element.attributeValue("specName"), "Heroics", out.append(")"); Integer.parseInt(element.attributeValue("score")), element.attributeValue("suggest").split(";"))); "Naxxramas 10", // and OS10 out.append("|"); } "Naxxramas 25", // and OS25/EoE10 out.append(toon.getGearScore()); "Ulduar 10", // and EoE25 boolean found = false; Collections.sort(toons); "Onyxia 10", } "Ulduar 25", // and ToTCr10 for (String raid : RAIDS) { finally { "Onyxia 25", if (toon.getRecommendedRaids().contains(raid)) { responseStream.close(); "Trial of the Crusader 25", out.append("|(!)"); } "Icecrown Citadel 10" found = true; return toons; }; } else { } out.append("|").append(found ? "(x)" : "(/)"); private static final String[] SHORT_RAIDS = { } public void setHttpRetrievalService(HttpRetrievalService httpRetrievalService) { "H", } this.httpRetrievalService = httpRetrievalService; "Naxx10/OS10", out.append("|n"); } "Naxx25/OS25/EoE10", } "Uld10/EoE25", public void setSubRenderer(SubRenderer subRenderer) { "Ony10", return subRenderer.render(out.toString(), renderContext); this.subRenderer = subRenderer; "Uld25/TotCr10", } } "Ony25", "TotCr25", private List<Toon> retrieveToons(String guildName, String realmName, String zone) public void setCacheManager(CacheManager cacheManager) { "IC" throws MacroException { this.cacheManager = cacheManager; }; String url = null; } ... ... } 19 19
  • 20. ...Plugin Tutorial: World of WarCraft... package  com.atlassian.confluence.plugins.gwowplugin ...        toons.each  {  toon  -­‐> import  com.atlassian.cache.CacheManager            def  url  =  "http://xml.wow-­‐heroes.com/index.php?zone=${enc  zone}&server=${enc  map.realm}&name=${enc  toon.name}" import  com.atlassian.confluence.util.http.HttpRetrievalService            out.append("|  [${toon.name}|${url}]  |  $toon.className  ($toon.spec)|  $toon.gearScore") import  com.atlassian.renderer.RenderContext            boolean  found  =  false import  com.atlassian.renderer.v2.RenderMode            RAIDS.each  {  raid  -­‐> import  com.atlassian.renderer.v2.SubRenderer                if  (raid  in  toon.recommendedRaids)  { import  com.atlassian.renderer.v2.macro.BaseMacro                    out.append("|(!)") import  com.atlassian.renderer.v2.macro.MacroException                    found  =  true                }  else  { /**                    out.append("|").append(found  ?  "(x)"  :  "(/)")  *  Inserts  a  table  of  a  guild's  roster  of  80s  ranked  by  gear  level,  with  recommended  raid                }  *  instances.  The  data  for  the  macro  is  grabbed  from  http://wow-­‐heroes.com.  Results  are            } 200 -> 90  *  cached  for  $DEFAULT_CACHE_LIFETIME  to  reduce  load  on  the  server.            out.append("|n")  *  <p/>        }  *  Usage:  {guild-­‐gear:realm=Nagrand|guild=A  New  Beginning|zone=us}        subRenderer.render(out.toString(),  renderContext)  */    } class  GuildGearMacro  extends  BaseMacro  {    HttpRetrievalService  httpRetrievalService    private  retrieveToons(String  guildName,  String  realmName,  String  zone)  throws  MacroException  {    SubRenderer  subRenderer        def  url  =  "http://xml.wow-­‐heroes.com/xml-­‐guild.php?z=${enc  zone}&r=${enc  realmName}&g=${enc  guildName}"    CacheManager  cacheManager        def  cache  =  cacheManager.getCache(this.class.name  +  ".toons")        if  (!cache.get(url))  cache.put(url,  retrieveAndParseFromWowArmory(url))    private  static  final  String[]  RAIDS  =  [        return  cache.get(url)                "Heroics",  "Naxxramas  10",  "Naxxramas  25",  "Ulduar  10",  "Onyxia  10",    }                "Ulduar  25",  "Onyxia  25",  "Trial  of  the  Crusader  25",  "Icecrown  Citadel  10"]    private  static  final  String[]  SHORT_RAIDS  =  [    private  retrieveAndParseFromWowArmory(String  url)  {                "H",  "Naxx10/OS10",  "Naxx25/OS25/EoE10",  "Uld10/EoE25",  "Ony10",        def  toons                "Uld25/TotCr10",  "Ony25",  "TotCr25",  "IC"]        httpRetrievalService.get(url).response.withReader  {  reader  -­‐>            toons  =  new  XmlSlurper().parse(reader).guild.character.collect  {    boolean  isInline()  {  false  }                new  Toon(    boolean  hasBody()  {  false  }                    name:                          it.@name,    RenderMode  getBodyRenderMode()  {  RenderMode.NO_RENDER  }                    classId:                    it.@classId.toInteger(),                    spec:                          it.@specName,    String  execute(Map  map,  String  s,  RenderContext  renderContext)  throws  MacroException  {                    gearScore:                it.@score.toInteger(),        def  zone  =  map.zone  ?:  "us"                    recommendedRaids:  it.@suggest.toString().split(";"))        def  out  =  new  StringBuilder("||Name||Class||Gear  Score")            }        SHORT_RAIDS.each  {  out.append("||").append(it.replace('/',  'n'))  }        }        out.append("||n")        toons.sort{  a,  b  -­‐>  a.gearScore  ==  b.gearScore  ?  a.name  <=>  b.name  :  a.gearScore  <=>  b.gearScore  }    }        def  toons  =  retrieveToons(map.guild,  map.realm,  zone) ...    def  enc(s)  {  URLEncoder.encode(s,  'UTF-­‐8')  } } 20 20
  • 21. ...Plugin Tutorial: World of WarCraft... {groovy-wow-item:1624} {groovy-guild-gear:realm=Kirin Tor|guild=Faceroll Syndicate|zone=us} 21 21
  • 22. ...Plugin Tutorial: World of WarCraft... > atlas-mvn clover2:setup test clover2:aggregate clover2:clover 22 22
  • 23. ...Plugin Tutorial: World of WarCraft narrative  'segment  flown',  { package  com.atlassian.confluence.plugins.gwowplugin        as_a  'frequent  flyer'        i_want  'to  accrue  rewards  points  for  every  segment  I  fly' class  ToonSpec  extends  spock.lang.Specification  {        so_that  'I  can  receive  free  flights  for  my  dedication  to  the  airline'        def  "successful  name  of  Toon  given  classId"()  { } scenario  'segment  flown',  {                given:        given  'a  frequent  flyer  with  a  rewards  balance  of  1500  points'                def  t  =  new  Toon(classId:  thisClassId)        when  'that  flyer  completes  a  segment  worth  500  points'        then  'that  flyer  has  a  new  rewards  balance  of  2000  points'                expect: }                t.className  ==  name scenario  'segment  flown',  {          given  'a  frequent  flyer  with  a  rewards  balance  of  1500  points',  {                where:                  flyer  =  new  FrequentFlyer(1500)                name              |    thisClassId          }                "Hunter"      |    3          when  'that  flyer  completes  a  segment  worth  500  points',  {                "Rogue"        |    4                  flyer.fly(new  Segment(500))          }                "Priest"      |    5          then  'that  flyer  has  a  new  rewards  balance  of  2000  points',  {                  flyer.pointsBalance.shouldBe  2000        }          } }•  } • Testing with Spock • Or Cucumber, EasyB, JBehave, 23 23
  • 24. Scripting on the fly... 24 24
  • 25. ...Scripting on the fly... 25 25
  • 26. ...Scripting on the fly 26 26
  • 27. 27 27