'분류 전체보기'에 해당되는 글 2491건

  1. 2014.02.22 Javascript Closures
  2. 2014.02.22 [펌]자바스크립트의 메모리관리
  3. 2014.02.07 [펌] Tomcat Context Reloader
  4. 2014.02.06 eclipse tomcat publish
  5. 2014.01.23 [펌] eclipse에서 pom.xml에 대한 Missing artifact 오류 발생시 해결방안
  6. 2014.01.22 [펌] Spring - Quartz를 사용하여 스케줄러 구현하기
  7. 2014.01.22 [펌] Spring mvc에서 Quartz 적용하기
  8. 2014.01.22 [펌] [Eclipse]Courier New 폰트
  9. 2014.01.20 [펌] 이클립스에서 메이븐 환경 구성하기
  10. 2014.01.15 Spring Cache namespace problem
  11. 2014.01.13 centos x window 설치
  12. 2014.01.13 Standard jQuery drag-and-drop
  13. 2014.01.13 [펌] DB 2차원배열List<Map> 형태의 데이터를 트리구조 JSON으로 변경하여 jquery.dynatree 표현 1
  14. 2014.01.10 [펌] spring 3.2 No setter found for property 'cronExpression' in class
  15. 2014.01.10 [펌] Eclipse에서 JavaDoc 생성시 javadoc unmappable character for encoding MS949 에러
  16. 2014.01.09 Dynatree documentation
  17. 2014.01.09 [펌][Maven] Repository (Remote, Central, Local)
  18. 2014.01.09 maven central repository
  19. 2014.01.09 m2eclipse error
  20. 2014.01.09 [펌] Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile (execution: default-testCompile, phase: test-compile)
  21. 2014.01.08 [펌] No grammar constraints (DTD or XML schema) detected for the document.
  22. 2014.01.08 [펌] Eclipse 에서 pom.xml 파일에 validation error mark
  23. 2014.01.08 [펌] eclipse Error - Can not find the tag library descriptor
  24. 2014.01.08 [펌] org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project bb: Compilation failure
  25. 2014.01.03 centos 6.x nvidia install driver
  26. 2014.01.03 [펌] [Eclipse, Maven, Tomcat] Maven으로 구성한 웹 프로젝트가 Eclipse에서 실행되지 않는 문제
  27. 2014.01.03 [링크] centos-ftp설치-및-설정
  28. 2014.01.03 [펌] eclipse 죽는문제 1
  29. 2014.01.03 [펌] Tomcat 설치 및 apache연동
  30. 2014.01.03 [펌] [CentOS6.2] jdk 설치
반응형

Javascript Closures

Introduction

Closure
A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

Closures are one of the most powerful features of ECMAScript (javascript) but they cannot be property exploited without understanding them. They are, however, relatively easy to create, even accidentally, and their creation has potentially harmful consequences, particularly in some relatively common web browser environments. To avoid accidentally encountering the drawbacks and to take advantage of the benefits they offer it is necessary to understand their mechanism. This depends heavily on the role of scope chains in identifier resolution and so on the resolution of property names on objects.

The simple explanation of a Closure is that ECMAScript allows inner functions; function definitions and function expressions that are inside the function bodes of other functions. And that those inner functions are allowed access to all of the local variables, parameters and declared inner functions within their outer function(s). A closure is formed when one of those inner functions is made accessible outside of the function in which it was contained, so that it may be executed after the outer function has returned. At which point it still has access to the local variables, parameters and inner function declarations of its outer function. Those local variables, parameter and function declarations (initially) have the values that they had when the outer function returned and may be interacted with by the inner function.

Unfortunately, properly understanding closures requires an understanding of the mechanism behind them, and quite a bit of technical detail. While some of the ECMA 262 specified algorithms have been brushed over in the early part of the following explanation, much cannot be omitted or easily simplified. Individuals familiar with object property name resolution may skip that section but only people already familiar with closures can afford to skip the following sections, and they can stop reading now and get back to exploiting them.

The Resolution of Property Names on Objects

ECMAScript recognises two categories of object, "Native Object" and "Host Object" with a sub-category of native objects called "Built-in Object" (ECMA 262 3rd Ed Section 4.3). Native objects belong to the language and host objects are provided by the environment, and may be, for example, document objects, DOM nodes and the like.

Native objects are loose and dynamic bags of named properties (some implementations are not that dynamic when it comes to the built in object sub-category, though usually that doesn't matter). The defined named properties of an object will hold a value, which may be a reference to another Object (functions are also Objects in this sense) or a primitive value: String, Number, Boolean, Null or Undefined. The Undefined primitive type is a bit odd in that it is possible to assign a value of Undefined to a property of an object but doing so does not remove that property from the object; it remains a defined named property, it just holds the value undefined.

The following is a simplified description of how property values are read and set on objects with the internal details brushed over to the greatest extent possible.

Assignment of Values

Named properties of objects can be created, or values set on existing named properties, by assigning a value to that named property. So given:-

var objectRef = new Object(); //create a generic javascript object.

A property with the name "testNumber" can be created as:-

objectRef.testNumber = 5;
/* - or:- */
objectRef["testNumber"] = 5;

The object had no "testNumber" property prior to the assignment but one is created when the assignment is made. Any subsequent assignment does not need to create the property, it just re-sets its value:-

objectRef.testNumber = 8;
/* - or:- */
objectRef["testNumber"] = 8;

Javascript objects have prototypes that can themselves be objects, as will be described shortly, and that prototype may have named properties. But this has no role in assignment. If a value is assigned and the actual object does not have a property with the corresponding name a property of that name is created and the value is assigned to it. If it has the property then its value is re-set.

Reading of Values

It is in reading values from object properties that prototypes come into play. If an object has a property with the property name used in the property accessor then the value of that property is returned:-

/* Assign a value to a named property. If the object does not have a
   property with the corresponding name prior to the assignment it
   will have one after it:-
*/
objectRef.testNumber = 8;

/* Read the value back from the property:- */

var val = objectRef.testNumber;
/* and  - val - now holds the value 8 that was just assigned to the
   named property of the object. */
   

But all objects may have prototypes, and prototypes are objects so they, in turn, may have prototypes, which may have prototypes, and so on forming what is called the prototype chain. The prototype chain ends when one of the objects in the chain has a null prototype. The default prototype for the Object constructor has a null prototype so:-

var objectRef = new Object(); //create a generic javascript object.

Creates an object with the prototype Object.prototype that itself has a null prototype. So the prototype chain for objectRef contains only one object: Object.prototype. However:-

/* A "constructor" function for creating objects of a -
   MyObject1 - type.
*/
function MyObject1(formalParameter){
    /* Give the constructed object a property called - testNumber - and
       assign it the value passed to the constructor as its first
       argument:-
    */
    this.testNumber = formalParameter;
}

/* A "constructor" function for creating objects of a -
   MyObject2 - type:-
*/
function MyObject2(formalParameter){
   /* Give the constructed object a property called - testString -
      and assign it the value passed to the constructor as its first
      argument:-
    */
    this.testString = formalParameter;
}

/* The next operation replaces the default prototype associated with
   all MyObject2 instances with an instance of MyObject1, passing the
   argument - 8 - to the MyObject1 constructor so that its -
   testNumber - property will be set to that value:-
*/
MyObject2.prototype = new MyObject1( 8 );

/* Finally, create an instance of - MyObject2 - and assign a reference
   to that object to the variable - objectRef - passing a string as the
   first argument for the constructor:-
*/

var objectRef = new MyObject2( "String_Value" );

The instance of MyObject2 referred to by the objectRef variable has a prototype chain. The first object in that chain is the instance of MyObject1 that was created and assigned to the prototype property of the MyObject2 constructor. The instance of MyObject1 has a prototype, the object that was assigned to the function MyObject1's prototype property by the implementation. That object has a prototype, the default Object prototype that corresponds with the object referred to by Object.prototype. Object.prototype has a null prototype so the prototype chain comes to an end at this point.

When a property accessor attempts to read a named property form the object referred to by the variable objectRef the whole prototype chain can enter into the process. In the simple case:-

var val = objectRef.testString;

- the instance of MyObject2 referred to by objectRef has a property with the name "testString" so it is the value of that property, set to "String_Value", that is assigned to the variable val. However:-

var val = objectRef.testNumber;

- cannot read a named property form the instance of MyObject2 itself as it has no such property but the variable val is set to the value of 8 rather than undefined because having failed to find a corresponding named property on the object itself the interpreter then examines the object that is its prototype. Its prototype is the instance of MyObject1 and it was created with a property named "testNumber" with the value 8 assigned to that property, so the property accessor evaluates as the value 8. Neither MyObject1 or MyObject2 have defined a toString method, but if a property accessor attempts to read the value of a toString property from objectRef:-

var val = objectRef.toString;

- the val variable is assigned a reference to a function. That function is the toString property of Object.prototype and is returned because the process of examining the prototype of objectRef, when objectRef turns out not to have a "toString" property, is acting on an object, so when that prototype is found to lack the property its prototype is examined in turn. Its prototype is Object.prototype, which does have a toString method so it is a reference to that function object that is returned.

Finally:-

var val = objectRef.madeUpProperty;

- returns undefined, because as the process of working up the prototype chain finds no properties on any of the object with the name "madeUpPeoperty" it eventually gets to the prototype of Object.prototype, which is null, and the process ends returning undefined.

The reading of named properties returns the first value found, on the object or then from its prototype chain. The assigning of a value to a named property on an object will create a property on the object itself if no corresponding property already exists.

This means that if a value was assigned as objectRef.testNumber = 3 a "testNumber" property will be created on the instance of MyObject2 itself, and any subsequent attempts to read that value will retrieve that value as set on the object. The prototype chain no longer needs to be examined to resolve the property accessor, but the instance of MyObject1 with the value of 8 assigned to its "testNumber" property is unaltered. The assignment to the objectRef object masks the corresponding property in its prototype chain.

Note: ECMAScript defines an internal [[prototype]] property of the internal Object type. This property is not directly accessible with scripts, but it is the chain of objects referred to with the internal [[prototype]] property that is used in property accessor resolution; the object's prototype chain. A public prototype property exists to allow the assignment, definition and manipulation of prototypes in association with the internal [[prototype]] property. The details of the relationship between to two are described in ECMA 262 (3rd edition) and are beyond the scope of this discussion.

Identifier Resolution, Execution Contexts and scope chains

The Execution Context

An execution context is an abstract concept used by the ECMSScript specification (ECMA 262 3rd edition) to define the behaviour required of ECMAScript implementations. The specification does not say anything about how execution contexts should be implemented but execution contexts have associated attributes that refer to specification defined structures so they might be conceived (and even implemented) as objects with properties, though not public properties.

All javascript code is executed in an execution context. Global code (code executed inline, normally as a JS file, or HTML page, loads) gets executed in global execution context, and each invocation of a function (possibly as a constructor) has an associated execution context. Code executed with the eval function also gets a distinct execution context but as eval is never normally used by javascript programmers it will not be discussed here. The specified details of execution contexts are to be found in section 10.2 of ECMA 262 (3rd edition).

When a javascript function is called it enters an execution context, if another function is called (or the same function recursively) a new execution context is created and execution enters that context for the duration of the function call. Returning to the original execution context when that called function returns. Thus running javascript code forms a stack of execution contexts.

When an execution context is created a number of things happen in a defined order. First, in the execution context of a function, an "Activation" object is created. The activation object is another specification mechanism. It can be considered as an object because it ends up having accessible named properties, but it is not a normal object as it has no prototype (at least not a defined prototype) and it cannot be directly referenced by javascript code.

The next step in the creation of the execution context for a function call is the creation of an arguments object, which is an array-like object with integer indexed members corresponding with the arguments passed to the function call, in order. It also has length and callee properties (which are not relevant to this discussion, see the spec for details). A property of the Activation object is created with the name "arguments" and a reference to the arguments object is assigned to that property.

Next the execution context is assigned a scope. A scope consists of a list (or chain) of objects. Each function object has an internal [[scope]] property (which we will go into more detail about shortly) that also consists of a list (or chain) of objects. The scope that is assigned to the execution context of a function call consists of the list referred to by the [[scope]] property of the corresponding function object with the Activation object added at the front of the chain (or the top of the list).

Then the process of "variable instantiation" takes place using an object that ECMA 262 refers to as the "Variable" object. However, the Activation object is used as the Variable object (note this, it is important: they are the same object). Named properties of the Variable object are created for each of the function's formal parameters, and if arguments to the function call correspond with those parameters the values of those arguments are assigned to the properties (otherwise the assigned value is undefined). Inner function definitions are used to create function objects which are assigned to properties of the Variable object with names that correspond to the function name used in the function declaration. The last stage of variable instantiation is to create named properties of the Variable object that correspond with all the local variables declared within the function.

The properties created on the Variable object that correspond with declared local variables are initially assigned undefined values during variable instantiation, the actual initialisation of local variables does not happen until the evaluation of the corresponding assignment expressions during the execution of the function body code.

It is the fact that the Activation object, with its arguments property, and the Variable object, with named properties corresponding with function local variables, are the same object, that allows the identifier arguments to be treated as if it was a function local variable.

Finally a value is assigned for use with the this keyword. If the value assigned refers to an object then property accessors prefixed with the this keyword reference properties of that object. If the value assigned (internally) is null then the this keyword will refer to the global object.

The global execution context gets some slightly different handling as it does not have arguments so it does not need a defined Activation object to refer to them. The global execution context does need a scope and its scope chain consists of exactly one object, the global object. The global execution context does go through variable instantiation, its inner functions are the normal top level function declarations that make up the bulk of javascript code. The global object is used as the Variable object, which is why globally declared functions become properties of the global object. As do globally declared variables.

The global execution context also uses a reference to the global object for the this object.

scope chains and [[scope]]

The scope chain of the execution context for a function call is constructed by adding the execution context's Activation/Variable object to the front of the scope chain held in the function object's [[scope]] property, so it is important to understand how the internal [[scope]] property is defined.

In ECMAScript functions are objects, they are created during variable instantiation from function declarations, during the evaluation of function expressions or by invoking the Function constructor.

Function objects created with the Function constructor always have a [[scope]] property referring to a scope chain that only contains the global object.

Function objects created with function declarations or function expressions have the scope chain of the execution context in which they are created assigned to their internal [[scope]] property.

In the simplest case of a global function declaration such as:-

function exampleFunction(formalParameter){
    ...   // function body code
}

- the corresponding function object is created during the variable instantiation for the global execution context. The global execution context has a scope chain consisting of only the global object. Thus the function object that is created and referred to by the property of the global object with the name "exampleFunction" is assigned an internal [[scope]] property referring to a scope chain containing only the global object.

A similar scope chain is assigned when a function expression is executed in the global context:-

var exampleFuncRef = function(){
    ...   // function body code
}

- except in this case a named property of the global object is created during variable instantiation for the global execution context but the function object is not created, and a reference to it assigned to the named property of the global object, until the assignment expression is evaluated. But the creation of the function object still happens in the global execution context so the [[scope]] property of the created function object still only contains the global object in the assigned scope chain.

Inner function declarations and expressions result in function objects being created within the execution context of a function so they get more elaborate scope chains. Consider the following code, which defines a function with an inner function declaration and then executes the outer function:-

function exampleOuterFunction(formalParameter){
    function exampleInnerFuncitonDec(){
        ... // inner function body
    }
    ...  // the rest of the outer function body.
}

exampleOuterFunction( 5 );

The function object corresponding with the outer function declaration is created during variable instantiation in the global execution context so its [[scope]] property contains the one item scope chain with only the global object in it.

When the global code executes the call to the exampleOuterFunction a new execution context is created for that function call and an Activation/Variable object along with it. The scope of that new execution context becomes the chain consisting of the new Activation object followed by the chain refereed to by the outer function object's [[scope]] property (just the global object). Variable instantiation for that new execution context results in the creation of a function object that corresponds with the inner function definition and the [[scope]] property of that function object is assigned the value of the scope from the execution context in which it was created. A scope chain that contains the Activation object followed by the global object.

So far this is all automatic and controlled by the structure and execution of the source code. The scope chain of the execution context defines the [[scope]] properties of the function objects created and the [[scope]] properties of the function objects define the scope for their execution contexts (along with the corresponding Activation object). But ECMAScript provides the with statement as a means of modifying the scope chain.

The with statement evaluates an expression and if that expression is an object it is added to the scope chain of the current execution context (in front of the Activation/Variable object). The with statement then executes another statement (that may itself be a block statement) and then restores the execution context's scope chainto what it was before.

A function declaration could not be affected by a with statement as they result in the creation of function objects during variable instantiation, but a function expression can be evaluated inside a with statement:-

/* create a global variable - y - that refers to an object:- */
var y = {x:5}; // object literal with an - x - property
function exampleFuncWith(){
    var z;
    /* Add the object referred to by the global variable - y - to the
       front of he scope chain:-
    */
    with(y){
        /* evaluate a function expression to create a function object
           and assign a reference to that function object to the local
           variable - z - :-
        */
        z = function(){
            ... // inner function expression body;
        }
    }
    ... 
}

/* execute the - exampleFuncWith - function:- */
exampleFuncWith();

When the exampleFuncWith function is called the resulting execution context has a scope chain consisting of its Activation object followed by the global object. The execution of the with statement adds the object referred to by the global variable y to the front of that scope chain during the evaluation of the function expression. The function object created by the evaluation of the function expression is assigned a [[scope]] property that corresponds with the scope of the execution context in which it is created. A scope chain consisting of object y followed by the Activation object from the execution context of the outer function call, followed by the global object.

When the block statement associated with the with statement terminates the scope of the execution context is restored (the y object is removed), but the function object has been created at that point and its [[scope]] property assigned a reference to a scope chain with the y object at its head.

Identifier Resolution

Identifiers are resolved against the scope chain. ECMA 262 categorises this as a keyword rather than an identifier, which is not unreasonable as it is always resolved dependent on the this value in the execution context in which it is used, without reference to the scope chain.

Identifier resolution starts with the first object in the scope chain. It is checked to see if it has a property with a name that corresponds with the identifier. Because the scope chain is a chain of objects this checking encompasses the prototype chain of that object (if it has one). If no corresponding value can be found on the first object in the scope chain the search progresses to the next object. And so on until one of the objects in the chain (or one of its prototypes) has a property with a name that corresponds with the identifier or the scope chain is exhausted.

The operation on the identifier happens in the same way as the use of property accessors on objects described above. The object identified in the scope chain as having the corresponding property takes the place of the object in the property accessor and the identifier acts as a property name for that object. The global object is always at the end of the scope chain.

As execution contexts associated with function calls will have the Activation/Variable object at the front of the chain, identifiers used in function bodies are effectively first checked to see whether they correspond with formal parameters, inner function declaration names or local variables. Those would be resolved as named properties of the Activation/Variable object.

Closures

Automatic Garbage Collection

ECMAScript uses automatic garbage collection. The specification does not define the details, leaving that to the implementers to sort out, and some implementations are known to give a very low priority to their garbage collection operations. But the general idea is that if an object becomes un-referable (by having no remaining references to it left accessible to executing code) it becomes available for garbage collection and will at some future point be destroyed and any resources it is consuming freed and returned to the system for re-use.

This would normally be the case upon exiting an execution context. The scope chain structure, the Activation/Variable object and any objects created within the execution context, including function objects, would no longer be accessible and so would become available for garbage collection.

Forming Closures

A closure is formed by returning a function object that was created within an execution context of a function call from that function call and assigning a reference to that inner function to a property of another object. Or by directly assigning a reference to such a function object to, for example, a global variable, a property of a globally accessible object or an object passed by reference as an argument to the outer function call. e.g:-

function exampleClosureForm(arg1, arg2){
    var localVar = 8;
    function exampleReturned(innerArg){
        return ((arg1 + arg2)/(innerArg + localVar));
    }
    /* return a reference to the inner function defined as -
       exampleReturned -:-
    */
    return exampleReturned;
}

var globalVar = exampleClosureForm(2, 4);

Now the function object created within the execution context of the call to exampleClosureForm cannot be garbage collected because it is referred to by a global variable and is still accessible, it can even be executed with globalVar(n).

But something a little more complicated has happened because the function object now referred to by globalVar was created with a [[scope]] property referring to a scope chain containing the Activation/Variable object belonging to the execution context in which it was created (and the global object). Now the Activation/Variable object cannot be garbage collected either as the execution of the function object referred to by globalVar will need to add the whole scope chain from its [[scope]] property to the scope of the execution context created for each call to it.

A closure is formed. The inner function object has the free variables and the Activation/Variable object on the function's scope chain is the environment that binds them.

The Activation/Variable object is trapped by being referred to in the scope chain assigned to the internal [[scope]] property of the function object now referred to by the globalVar variable. The Activation/Variable object is preserved along with its state; the values of its properties. Scope resolution in the execution context of calls to the inner function will resolve identifiers that correspond with named properties of that Activation/Variable object as properties of that object. The value of those properties can still be read and set even though the execution context for which it was created has exited.

In the example above that Activation/Variable object has a state that represents the values of formal parameters, inner function definitions and local variables, at the time when the outer function returned (exited its execution context). The arg1 property has the value 2,the arg2 property the value 4, localVar the value 8 and an exampleReturned property that is a reference to the inner function object that was returned form the outer function. (We will be referring to this Activation/Variable object as "ActOuter1" in later discussion, for convenience.)

If the exampleClosureForm function was called again as:-

var secondGlobalVar = exampleClosureForm(12, 3);

- a new execution context would be created, along with a new Activation object. And a new function object would be returned, with its own distinct [[scope]] property referring to a scope chain containing the Activation object form this second execution context, with arg1 being 12 and arg2 being 3. (We will be referring to this Activation/Variable object as "ActOuter2" in later discussion, for convenience.)

A second and distinct closure has been formed by the second execution of exampleClosureForm.

The two function objects created by the execution of exampleClosureForm to which references have been assigned to the global variable globalVar and secondGlobalVar respectively, return the expression ((arg1 + arg2)/(innerArg + localVar)). Which applies various operators to four identifiers. How these identifiers are resolved is critical to the use and value of closures.

Consider the execution of the function object referred to by globalVar, as globalVar(2). A new execution context is created and an Activation object (we will call it "ActInner1"), which is added to the head of the scope chain referred to the [[scope]] property of the executed function object. ActInner1 is given a property named innerArg, after its formal parameter and the argument value 2 assigned to it. The scope chain for this new execution context is: ActInner1-> ActOuter1-> global object.

Identifier resolution is done against the scope chain so in order to return the value of the expression ((arg1 + arg2)/(innerArg + localVar)) the values of the identifiers will be determined by looking for properties, with names corresponding with the identifiers, on each object in the scope chain in turn.

The first object in the chain is ActInner1 and it has a property named innerArg with the value 2. All of the other 3 identifiers correspond with named properties of ActOuter1; arg1 is 2, arg2 is 4 and localVar is 8. The function call returns ((2 + 4)/(2 + 8)).

Compare that with the execution of the otherwise identical function object referred to by secondGlobalVar, as secondGlobalVar(5). Calling the Activation object for this new execution context "ActInner2", the scope chain becomes: ActInner2-> ActOuter2-> global object. ActInner2 returns innerArg as 5 and ActOuter2 returns arg1, arg2 and localVar as 12, 3 and 8 respectively. The value returned is ((12 + 3)/(5 + 8)).

Execute secondGlobalVar again and a new Activation object will appear at the front of the scope chain but ActOuter2 will still be next object in the chain and the value of its named properties will again be used in the resolution of the identifiers arg1, arg2 and localVar.

This is how ECMAScript inner functions gain, and maintain, access to the formal parameters, declared inner functions and local variables of the execution context in which they were created. And it is how the forming of a closure allows such a function object to keep referring to those values, reading and writing to them, for as long as it continues to exist. The Activation/Variable object from the execution context in which the inner function was created remains on the scope chain referred to by the function object's [[scope]] property, until all references to the inner function are freed and the function object is made available for garbage collection (along with any now unneeded objects on its scope chain).

Inner function may themselves have inner functions, and the inner functions returned from the execution of functions to form closures may themselves return inner functions and form closures of their own. With each nesting the scope chain gains extra Activation objects originating with the execution contexts in which the inner function objects were created. The ECMAScript specification requires a scope chain to be finite, but imposes no limits on their length. Implementations probably do impose some practical limitation but no specific magnitude has yet been reported. The potential for nesting inner functions seems so far to have exceeded anyone's desire to code them.

What can be done with Closures?

Strangely the answer to that appears to be anything and everything. I am told that closures enable ECMAScript to emulate anything, so the limitation is the ability to conceive and implement the emulation. That is a bit esoteric and it is probably better to start with something a little more practical.

Example 1: setTimeout with Function References

A common use for a closure is to provide parameters for the execution of a function prior to the execution of that function. For example, when a function is to be provided as the first argument to the setTimout function that is common in web browser environments.

setTimeout schedules the execution of a function (or a string of javascript source code, but not in this context), provided as its first argument, after an interval expressed in milliseconds (as its second argument). If a piece of code wants to use setTimeout it calls the setTimeout function and passes a reference to a function object as the first argument and the millisecond interval as the second, but a reference to a function object cannot provide parameters for the scheduled execution of that function.

However, code could call another function that returned a reference to an inner function object, with that inner function object being passed by reference to the setTimeout function. The parameters to be used for the execution of the inner function are passed with the call to the function that returns it. setTimout executes the inner function without passing arguments but that inner function can still access the parameters provided by the call to the outer function that returned it:-

function callLater(paramA, paramB, paramC){
    /* Return a reference to an anonymous inner function created
       with a function expression:-
    */
    return (function(){
        /* This inner function is to be executed with - setTimeout
           - and when it is executed it can read, and act upon, the
           parameters passed to the outer function:-
        */
        paramA[paramB] = paramC;
    });
}

...

/* Call the function that will return a reference to the inner function
   object created in its execution context. Passing the parameters that
   the inner function will use when it is eventually executed as
   arguments to the outer function. The returned reference to the inner
   function object is assigned to a local variable:-
*/
var functRef = callLater(elStyle, "display", "none");
/* Call the setTimeout function, passing the reference to the inner
   function assigned to the - functRef - variable as the first argument:-
*/
hideMenu=setTimeout(functRef, 500);

Example 2: Associating Functions with Object Instance Methods

There are many other circumstances when a reference to a function object is assigned so that it would be executed at some future time where it is useful to provide parameters for the execution of that function that would not be easily available at the time of execution but cannot be known until the moment of assignment.

One example might be a javascript object that is designed to encapsulate the interactions with a particular DOM element. It has doOnClick, doMouseOver and doMouseOut methods and wants to execute those methods when the corresponding events are triggered on the DOM element, but there may be any number of instances of the javascript object created associated with different DOM elements and the individual object instances do not know how they will be employed by the code that instantiated them. The object instances do not know how to reference themselves globally because they do not know which global variables (if any) will be assigned references to their instances.

So the problem is to execute an event handling function that has an association with a particular instance of the javascript object, and knows which method of that object to call.

The following example uses a small generalised closure based function that associates object instances with element event handlers. Arranging that the execution of the event handler calls the specified method of the object instance, passing the event object and a reference to the associated element on to the object method and returning the method's return value.

/* A general function that associates an object instance with an event
   handler. The returned inner function is used as the event handler.
   The object instance is passed as the - obj - parameter and the name
   of the method that is to be called on that object is passed as the -
   methodName - (string) parameter.
*/
function associateObjWithEvent(obj, methodName){
    /* The returned inner function is intended to act as an event
       handler for a DOM element:-
    */
    return (function(e){
        /* The event object that will have been parsed as the - e -
           parameter on DOM standard browsers is normalised to the IE
           event object if it has not been passed as an argument to the
           event handling inner function:-
        */
        e = e||window.event;
        /* The event handler calls a method of the object - obj - with
           the name held in the string - methodName - passing the now
           normalised event object and a reference to the element to
           which the event handler has been assigned using the - this -
           (which works because the inner function is executed as a
           method of that element because it has been assigned as an
           event handler):-
        */
        return obj[methodName](e, this);
    });
}

/* This constructor function creates objects that associates themselves
   with DOM elements whose IDs are passed to the constructor as a
   string. The object instances want to arrange than when the
   corresponding element triggers onclick, onmouseover and onmouseout
   events corresponding methods are called on their object instance.
*/
function DhtmlObject(elementId){
    /* A function is called that retrieves a reference to the DOM
       element (or null if it cannot be found) with the ID of the
       required element passed as its argument. The returned value
       is assigned to the local variable - el -:-
    */
    var el = getElementWithId(elementId);
    /* The value of - el - is internally type-converted to boolean for
       the - if - statement so that if it refers to an object the
       result will be true, and if it is null the result false. So that
       the following block is only executed if the - el - variable
       refers to a DOM element:-
    */
    if(el){
        /* To assign a function as the element's event handler this
           object calls the - associateObjWithEvent - function
           specifying itself (with the - this - keyword) as the object
           on which a method is to be called and providing the name of
           the method that is to be called. The - associateObjWithEvent
           - function will return a reference to an inner function that
           is assigned to the event handler of the DOM element. That
           inner function will call the required method on the
           javascript object when it is executed in response to
           events:-
        */
        el.onclick = associateObjWithEvent(this, "doOnClick");
        el.onmouseover = associateObjWithEvent(this, "doMouseOver");
        el.onmouseout = associateObjWithEvent(this, "doMouseOut");
        ...
    }
}
DhtmlObject.prototype.doOnClick = function(event, element){
    ... // doOnClick method body.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
    ... // doMouseOver method body.
}
DhtmlObject.prototype.doMouseOut = function(event, element){
    ... // doMouseOut method body.
}

And so any instances of the DhtmlObject can associate themselves with the DOM element that they are interested in without any need to know anything about how they are being employed by other code, impacting on the global namespace or risking clashes with other instances of the DhtmlObject.

Example 3: Encapsulating Related Functionality

Closures can be used to create additional scopes that can be used to group interrelated and dependent code in a way that minimises the risk of accidental interaction. Suppose a function is to build a string and to avoid the repeated concatenation operations (and the creation of numerous intermediate strings) the desire is to use an array to store the parts of the string in sequence and then output the results using the Array.prototype.join method (with an empty string as its argument). The array is going to act as a buffer for the output, but defining it locally to the function will result in its re-creation on each execution of the function, which may not be necessary if the only variable content of that array will be re-assigned on each function call.

One approach might make the array a global variable so that it can be re-used without being re-created. But the consequences of that will be that, in addition to the global variable that refers to the function that will use the buffer array, there will be a second global property that refers to the array itself. The effect is to render the code less manageable, as, if it is to be used elsewhere, its author has to remember to include both the function definition and the array definition. It also makes the code less easy to integrate with other code because instead of just ensuring that the function name is unique within the global namespace it is necessary to ensure that the Array on which it is dependent is using a name that is unique within the global namespace.

A Closure allows the buffer array to be associated (and neatly packaged) with the function that is dependent upon it and simultaneously keep the property name to which the buffer array as assigned out of the global namespace and free of the risk of name conflicts and accidental interactions.

The trick here is to create one additional execution context by executing a function expression in-line and have that function expression return an inner function that will be the function that is used by external code. The buffer array is then defined as a local variable of the function expression that is executed in-line. That only happens once so the Array is only created once, but is available to the function that depends on it for repeated use.

The following code creates a function that will return a string of HTML, much of which is constant, but those constant character sequences need to be interspersed with variable information provided as parameter to the function call.

A reference to an inner function object is returned from the in-line execution of a function expression and assigned to a global variable so that it can be called as a global function. The buffer array is defined as a local variable in the outer function expression. It is not exposed in the global namespace and does not need to be re-created whenever the function that uses it is called.

/* A global variable - getImgInPositionedDivHtml - is declared and
   assigned the value of an inner function expression returned from
   a one-time call to an outer function expression.

   That inner function returns a string of HTML that represents an
   absolutely positioned DIV wrapped round an IMG element, such that
   all of the variable attribute values are provided as parameters
   to the function call:-
*/
var getImgInPositionedDivHtml = (function(){
    /* The - buffAr - Array is assigned to a local variable of the
       outer function expression. It is only created once and that one
       instance of the array is available to the inner function so that
       it can be used on each execution of that inner function.

       Empty strings are used as placeholders for the date that is to
       be inserted into the Array by the inner function:-
    */
    var buffAr = [
        '<div id="',
        '',   //index 1, DIV ID attribute
        '" style="position:absolute;top:',
        '',   //index 3, DIV top position
        'px;left:',
        '',   //index 5, DIV left position
        'px;width:',
        '',   //index 7, DIV width
        'px;height:',
        '',   //index 9, DIV height
        'px;overflow:hidden;\"><img src=\"',
        '',   //index 11, IMG URL
        '\" width=\"',
        '',   //index 13, IMG width
        '\" height=\"',
        '',   //index 15, IMG height
        '\" alt=\"',
        '',   //index 17, IMG alt text
        '\"><\/div>'
    ];
    /* Return the inner function object that is the result of the
       evaluation of a function expression. It is this inner function
       object that will be executed on each call to -
       getImgInPositionedDivHtml( ... ) -:-
    */
    return (function(url, id, width, height, top, left, altText){
        /* Assign the various parameters to the corresponding
           locations in the buffer array:-
        */
        buffAr[1] = id;
        buffAr[3] = top;
        buffAr[5] = left;
        buffAr[13] = (buffAr[7] = width);
        buffAr[15] = (buffAr[9] = height);
        buffAr[11] = url;
        buffAr[17] = altText;
        /* Return the string created by joining each element in the
           array using an empty string (which is the same as just
           joining the elements together):-
        */
        return buffAr.join('');
    }); //:End of inner function expression.
})();
/*^^- :The inline execution of the outer function expression. */

If one function was dependent on one (or several) other functions, but those other functions were not expected to be directly employed by any other code, then the same technique could be used to group those functions with the one that was to be publicly exposed. Making a complex multi-function process into an easily portable and encapsulated unit of code.

Other Examples

Probably one of the best known applications of closures is Douglas Crockford's technique for the emulation of private instance variables in ECMAScript objects. Which can be extended to all sorts of structures of scope contained nested accessibility/visibility, including the emulation of private static members for ECMAScript objects.

The possible application of closures are endless, understanding how they work is probably the best guide to realising how they can be used.

Accidental Closures

Rendering any inner function accessible outside of the body of the function in which it was created will form a closure. That makes closures very easy to create and one of the consequences is that javascript authors who do not appreciate closures as a language feature can observe the use of inner functions for various tasks and employ inner functions, with no apparent consequences, not realising that closures are being created or what the implications of doing that are.

Accidentally creating closures can have harmful side effects as the following section on the IE memory leak problem describes, but they can also impact of the efficiency of code. It is not the closures themselves, indeed carefully used they can contribute significantly towards the creation of efficient code. It is the use of inner functions that can impact on efficiency.

A common situation is where inner functions are used is as event handlers for DOM elements. For example the following code might be used to add an onclick handler to a link element:-

/* Define the global variable that is to have its value added to the
   - href - of a link as a query string by the following function:-
*/
var quantaty = 5;
/* When a link passed to this function (as the argument to the function
   call - linkRef -) an onclick event handler is added to the link that
   will add the value of a global variable - quantaty - to the - href -
   of that link as a query string, then return true so that the link
   will navigate to the resource specified by the - href - which will
   by then include the assigned query string:-
*/
function addGlobalQueryOnClick(linkRef){
    /* If the - linkRef - parameter can be type converted to true
       (which it will if it refers to an object):-
    */
    if(linkRef){
        /* Evaluate a function expression and assign a reference to the
           function object that is created by the evaluation of the
           function expression to the onclick handler of the link
           element:-
        */
        linkRef.onclick = function(){
            /* This inner function expression adds the query string to
               the - href - of the element to which it is attached as
               an event handler:-
            */
            this.href += ('?quantaty='+escape(quantaty));
            return true;
        };
    }
}

Whenever the addGlobalQueryOnClick function is called a new inner function is created (and a closure formed by its assignment). From the efficiency point of view that would not be significant if the addGlobalQueryOnClick function was only called once or twice, but if the function was heavily employed many distinct function objects would be created (one for each evaluation of the inner function expression).

The above code is not taking advantage of the fact that inner functions are becoming accessible outside of the function in which they are being created (or the resulting closures). As a result exactly the same effect could be achieved by defining the function that is to be used as the event handler separately and then assigning a reference to that function to the event handling property. Only one function object would be created and all of the elements that use that event handler would share a reference to that one function:-

/* Define the global variable that is to have its value added to the
   - href - of a link as a query string by the following function:-
*/
var quantaty = 5;

/* When a link passed to this function (as the argument to the function
   call - linkRef -) an onclick event handler is added to the link that
   will add the value of a global variable - quantaty - to the - href -
   of that link as a query string, then return true so that the link
   will navigate to the resource specified by the - href - which will
   by then include the assigned query string:-
*/
function addGlobalQueryOnClick(linkRef){
    /* If the - linkRef - parameter can be type converted to true
       (which it will if it refers to an object):-
    */
    if(linkRef){
        /* Assign a reference to a global function to the event
           handling property of the link so that it becomes the
           element's event handler:-
        */
        linkRef.onclick = forAddQueryOnClick;
    }
}
/* A global function declaration for a function that is intended to act
   as an event handler for a link element, adding the value of a global
   variable to the - href - of an element as an event handler:-
*/
function forAddQueryOnClick(){
    this.href += ('?quantaty='+escape(quantaty));
    return true;
}

As the inner function in the first version is not being used to exploit the closures produced by its use, it would be more efficient not to use an inner function, and thus not repeat the process of creating many essentially identical function objects.

A similar consideration applies to object constructor functions. It is not uncommon to see code similar to the following skeleton constructor:-

function ExampleConst(param){
    /* Create methods of the object by evaluating function expressions
       and assigning references to the resulting function objects
       to the properties of the object being created:-
    */
    this.method1 = function(){
        ... // method body.
    };
    this.method2 = function(){
        ... // method body.
    };
    this.method3 = function(){
        ... // method body.
    };
    /* Assign the constructor's parameter to a property of the object:-
    */
    this.publicProp = param;
}

Each time the constructor is used to create an object, with new ExampleConst(n), a new set of function objects are created to act as its methods. So the more object instances that are created the more function objects are created to go with them.

Douglas Crockford's technique for emulating private members on javascript objects exploits the closure resulting form assigning references to inner function objects to the public properties of a constructed object from within its constructor. But if the methods of an object are not taking advantage of the closure that they will form within the constructor the creation of multiple function objects for each object instantiation will make the instantiation process slower and more resources will be consumed to accommodate the extra function objects created.

In that case it would be more efficient to create the function object once and assign references to them to the corresponding properties of the constructor's prototype so they may be shared by all of the objects created with that constructor:-

function ExampleConst(param){
    /* Assign the constructor's parameter to a property of the object:-
    */
    this.publicProp = param;
}
/* Create methods for the objects by evaluating function expressions
   and assigning references to the resulting function objects to the
   properties of the constructor's prototype:-
*/
ExampleConst.prototype.method1 = function(){
    ... // method body.
};
ExampleConst.prototype.method2 = function(){
    ... // method body.
};
ExampleConst.prototype.method3 = function(){
    ... // method body.
};

The Internet Explorer Memory Leak Problem

The Internet Explorer web browser (verified on versions 4 to 6 (6 is current at the time of writing)) has a fault in its garbage collection system that prevents it from garbage collecting ECMAScript and some host objects if those host objects form part of a "circular" reference. The host objects in question are any DOM Nodes (including the document object and its descendants) and ActiveX objects. If a circular reference is formed including one or more of them, then none of the objects involved will be freed until the browser is closed down, and the memory that they consume will be unavailable to the system until that happens.

A circular reference is when two or more objects refer to each other in a way that can be followed and lead back to the starting point. Such as object 1 has a property that refers to object 2, object 2 has a property that refers to object 3 and object 3 has a property that refers back to object 1. With pure ECMAScript objects as soon as no other objects refer to any of objects 1, 2 or 3 the fact that they only refer to each other is recognised and they are made available for garbage collection. But on Internet Explorer, if any of those objects happen to be a DOM Node or ActiveX object, the garbage collection cannot see that the circular relationship between them is isolated from the rest of the system and free them. Instead they all stay in memory until the browser is closed.

Closures are extremely good at forming circular references. If a function object that forms a closure is assigned as, for example, and event handler on a DOM Node, and a reference to that Node is assigned to one of the Activation/Variable objects in its scope chain then a circular reference exists. DOM_Node.onevent -> function_object.[[scope]] -> scope_chain -> Activation_object.nodeRef -> DOM_Node. It is very easy to do, and a bit of browsing around a site that forms such a reference in a piece of code common to each page can consume most of the systems memory (possibly all).

Care can be taken to avoid forming circular references and remedial action can be taken when they cannot otherwise be avoided, such as using IE's onunload event to null event handling function references. Recognising the problem and understanding closures (and their mechanism) is the key to avoiding this problem with IE.

comp.lang.javascript FAQ notes T.O.C.

  • Written by Richard Cornford. March 2004.
  • With corrections and suggestions by:-
    • Martin Honnen.
    • Yann-Erwan Perio (Yep).
    • Lasse Reichstein Nielsen. (definition of closure)
    • Mike Scirocco.
    • Dr John Stockton.
    • Garrett Smith.

 

Posted by 1010
반응형

자바스크립트의 메모리관리

개요

C 언어같은 저급 언어는 메모리 관리를 위해 malloc() 과 free()를 사용한다. 반면, 자바스크립트는 무언가가 생성되었을 때(오브젝트나 문자열 등) 메모리를 할당하고 쓸모 없어졌을 때 '자동으로' free 한다. '자동으로' 라는 말에는 혼란의 여지가 있다. 이는 자바스크립트를 포함한 여러 고급 언어 개발자들에게 메모리 관리가 불가능하다는 인상을 준다. 하지만 실상은 그렇지 않다.  

메모리 생존주기

메모리 생존주기는 프로그래밍 언어와 관계없이 비슷하다.

  1. 필요할때 할당한다.
  2. 사용한다. (읽기, 쓰기)
  3. 필요없어지면 해제한다. 

첫 번째 부분과 두 번째 부분은 모든 언어에서 분명하게 기술되지만 마지막 부분은 조금 다르다. 저급 언어에서는 분명히 기술되지만 자바스크립트 같은 고급 언어에서는 분명하게 기술되지 않는다(역자: 명시적으로 free를 하지 않는다는 의미). 

자바스크립트에서 메모리 할당

값 초기화

자바스크립트에서는 프로그래머들이 일일히 메모리 할당을 하는 수고를 덜어주기위해 값을 선언할 때 메모리를 할당한다. 

var n = 123; // 정수를 담기 위한 메모리 할당
var s = "azerty"; // 문자열을 담기 위한 메모리 할당

var o = {
  a: 1,
  b: null
}; // 오브젝트와 그 오브젝트에 포함된 값들을 담기 위한 메모리 할당

var a = [1, null, "abra"]; // (오브젝트 처럼) 배열과 배열에 담긴 값들을 위한 메모리 할당

function f(a){
  return a + 2;
} // 함수를 위한 할당(함수는 '호출가능한' 오브젝트이다)

// 함수식 또한 오브젝트를 담기위한 메모리를 할당한다. 
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);

함수 호출을 통한 할당

몇 가지 함수에서도 메모리 할당이 일어난다. 

var d = new Date();
var e = document.createElement('div'); // DOM 엘리먼트를 위해 메모리를 할당한다.

몇 가지 메쏘드도 새로운 값이나 오브젝트를 담기 위해 메모리 할당이 일어난다.

var s = "azerty";
var s2 = s.substr(0, 3); // s2는 새로운 문자열
// 자바스크립트에서 문자열은 immutable 값이기 때문에 메모리를 새로 할당하지 않고 단순히 [0, 3] 이라는 범위만 저장한다. 

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 4개의 원소를 가진 새로운 배열

값 사용

값 사용이란 기본적으로는 할당된 메모리를 읽고 쓰는 것을 의미한다. 변수나 오브젝트 속성 값을 읽고 쓸때 값 사용이 일어난다. 또 함수 호출시 함수에 인수를 넘길때도 일어난다. 

할당된 메모리가 더 이상 필요없을 때 해제하기

이 단계에서 대부분의 문제가 발생한다. "할당된 메모리가 더 이상 필요없을 때"를 알아내기가 힘들기 때문이다. 이제까지는 개발자들이 메모리가 필요없어질 때를 정하고 free하곤 했다. 

고급 언어 인터프리터는 "가비지 콜렉터"라는 소프트웨어를 가지고 있다. 가비지 콜렉터란 메모리 할당을 추적하고 할당된 메모리가 더 이상 필요 없어졌을 때 해제하는 작업을 한다. 이 작업은 근사적인 작업이다. 왜냐하면 일반적인 경우에 어떤 메모리가 필요없는지 알아내는 것은 알고리즘으로 풀 수 없는 비결정적인 문제이기 때문이다. (역자: 세상에 존재하는 모든 가비지 콜렉터는 안전하지만 완전하지 않다. 가비지 콜렉터는 항상 필요없어진 메모리만을 해제하지만 모든 필요없어진 메모리를 해제하는건 아니다)

가비지 콜렉션

위에서 언급한 것처럼 "더 이상 필요없는" 모든 메모리를 찾는건 비결정적이다. 따라서 몇 가지 제한을 두어 "더 이상 필요없는 모든 메모리"가 아니라 "더 이상 필요없는 몇몇 메모리"를 찾아보자. 몇 개의 가비지 콜렉션 알고리즘을 소개하고 한계점을 알아볼 것이다.

참조

가비지 콜렉션 알고리즘의 핵심 개념은 참조이다. A라는 메모리를 통해 (명시적이든 암시적이든) B라는 메모리에 접근할 수 있다면 "B는 A에 참조된다" 라고 한다. 예를 들어 모든 자바스크립트 오브젝트는 prototype 을 암시적으로 참조하고 그 오브젝트의 속성을 명시적으로 참조한다.

앞으로 "오브젝트"라는 어휘의 의미를 넓혀서 기존의 자바스크립트 오브젝트뿐만 아니라 함수 스코프도 포괄하자.

참조-세기(Reference-counting) 가비지 콜렉션

참조-세기 알고리즘은 가장 무난한 알고리즘이다. 이 알고리즘은 "더 이상 필요없는 오브젝트"를 "어떤 다른 오브젝트도 참조하지 않는 오브젝트"라고 정의한다. 어떤 오브젝트를 참조하는 다른 오브젝트가 하나도 없다면 그 오브젝트에 대해 가비지 콜렉션을 수행한다.

예제

var o = { 
  a: {
    b:2
  }
}; // 2개의 오브젝트가 생성되었다. 하나의 오브젝트는 다른 오브젝트의 속성으로 참조된다.
// 나머지 하나는 'o' 변수에 할당되었다.
// 명백하게 가비지 콜렉션 수행될 메모리는 하나도 없다.


var o2 = o; // 'o2' 변수는 위의 오브젝트를 참조하는 두 번째 변수이다.
o = 1; // 이제 'o2' 변수가 위의 오브젝트를 참조하는 유일한 변수가 되었다.

var oa = o2.a; // 위의 오브젝트의 'a' 속성을 참조했다.
// 이제 'o2.a'는 두 개의 참조를 가진다. 'o2'가 속성으로 참조하고 'oa'라는 변수가 참조한다.

o2 = "yo"; // 이제 맨 처음 'o' 변수가 참조했던 오브젝트를 참조하는 오브젝트는 없다(역자: 참조하는 유일한 변수였던 o2에 다른 값을 대입했다)
// 이제 오브젝트에 가비지 콜렉션이 수행될 수 있을까?
// 아니다. 오브젝트의 'a' 속성이 여전히 'oa' 변수에 의해 참조되므로 메모리를 해제할 수 없다.

oa = null; // 'oa' 변수에 다른 값을 할당했다. 이제 맨 처음 'o' 변수가 참조했던 오브젝트를 참조하는 다른 변수는 없으므로 가비지 콜렉션이 수행된다.

한계: 순환

이 알고리즘은 두 오브젝트가 서로를 참조하면 문제가 발생한다. 두 오브젝트 모두 필요 없어졌더라도 가비지 콜렉션을 수행할 수 없다.

function f(){
  var o = {};
  var o2 = {};
  o.a = o2; // o는 o2를 참조한다.
  o2.a = o; // o2는 o를 참조한다.

  return "azerty";
}

f();
// 두 오브젝트가 만들어지고 서로를 참조해서 순환이 일어났다.
// 함수가 종료되고 나면 사실상 두 오브젝트는 의미가 없어지므로 가비지 콜렉션이 수행되어야 한다.
// 그러나 위의 참조-세기 알고리즘에서는 두 오브젝트 모두 참조를 가지고 있기 때문에 둘 다 가비지 콜렉션이 일어나지 않는다.

실제 예제

인터넷 익스플로러 6, 7 은 DOM 오브젝트에 대해 참조-세기 알고리즘으로 가비지 콜렉션을 수행한다. 흔히, 이 두 브라우저에서는 다음과 같은 패턴의 메모리 누수가 발생한다. 

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div 오브젝트는 이벤트 핸들러를 'onclick' 속성을 통해 참조한다.
// 이벤트 핸들러의 스코프에도 div 오브젝트가 있으므로 div 오브젝트에 접근할 수 있다. 따라서 이벤트 핸들러도 div 오브젝트를 참조한다.
// 순환이 발생했고 메모리 누수가 일어난다.

표시하고-쓸기(Mark-and-sweep) 알고리즘

이 알고리즘은 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의한다.

이 알고리즘은 roots 라는 오브젝트의 집합을 가지고 있다(자바스크립트에서는 전역 변수들을 의미한다). 주기적으로 가비지 콜렉터는 roots로 부터 시작하여 roots가 참조하는 오브젝트들, roots가 참조하는 오브젝트가 참조하는 오브젝트들... 을 닿을 수 있는 오브젝트라고 표시한다. 그리고 닿을 수 있는 오브젝트가 아닌 닿을 수 없는 오브젝트에 대해 가비지 콜렉션을 수행한다.

이 알고리즘은 위에서 설명한 참조-세기 알고리즘보다 효율적이다. 왜냐하면 "참조되지 않는 오브젝트"는 모두 "닿을 수 없는 오브젝트" 이지만 역은 성립하지 않기 때문이다. 위에서 반례인 순환 참조하는 오브젝트들을 설명했다.

2012년 기준으로 모든 최신 브라우저들은 가비지 콜렉션에서 표시하고-쓸기 알고리즘을 사용한다. 지난 몇 년간 연구된 자바스크립트 가비지 콜렉션 알고리즘의 개선들은 모두 이 알고리즘에 대한 것이다. 개선된 알고리즘도 여전히 "더 이상 필요없는 오브젝트"를 "닿을 수 없는 오브젝트"로 정의하고 있다.

순환 참조는 이제 문제가 되지 않는다.

첫 번째 예제에서 함수가 리턴되고 나서 두 오브젝트는 닿을 수 없다. 따라서 가비지 콜렉션이 일어난다.

두 번째 예제에서도 마찬가지다. div 변수와 이벤트 핸들러가 roots로 부터 닿을 수 없어지면 순환 참조가 일어났음에도 불구하고 가비지 콜렉션이 일어난다.

한계: 오브젝트들은 명시적으로 닿을 수 없어져야 한다.

이 한계가 지적되었지만 실제로는 사람들은 이 문제를 비롯한 가비지 콜렉션에 별 관심이 없다.

더 보기

출처 : https://developer.mozilla.org/ko/docs/JavaScript/Memory_Management

Posted by 1010
반응형

출처 : http://kwonnam.pe.kr/wiki/java/tomcat/contextreload

Tomcat Context Reloader

Tomcat의 컨텍스트를 reloadable=“false”인 상태에서, Tomcat Manager를 설치하지 않은 상태에서 수동 Reload 할 수 있는 Valve를 만들어 보았다. Reload Tomcat Context manually(without manager or reloadable=“true” option).

설정

  • Tomcat Reload Valve 소스와 Jar 파일에서 tomcatreloadvalve.jar 파일을 $CATALINA_HOME/lib 로 복사한다.
  • server.xml 혹은 context.xml의 <Context> 항목에 Valve를 추가한다. 항상 <Context> 항목에만 추가해야 한다.
    <Context docBase="some" path="/some" reloadable="false" >
        <Valve className="kr.pe.kwonnam.tomcat.reloader.TomcatReloadValve"/>
    </Context>
  • reloadable=“false”로 둔다. 원래 이 Valve의 목적은 자동 Reloading을 끄고, 항상 수동으로 원하는 경우에만 Reloading하는 것이다.

실행

  • 웹브라우저 혹은 wget 등으로 http://localhost:8080/reloadContext 를 호출한다.
  • 실제 URL의 도메인네임 부분은 자신의 톰캣 설정을 따른다.
  • “Context Reloaded!!” 메시지가 나오면서 Reloading이 완료된다.

소스

package kr.pe.kwonnam.tomcat.reloader;
 
import java.io.IOException;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
 
import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
 
/**
 * Reload Tomcat Context by requesting URL
 *
 * Context의 reloadable="false"인 상태에서도 /reloadContext URL을 호출하면 해당 컨텍스트가 Reloading 된다.
 *
 * @author 손권남 kwon37xi@gmail.com
 *
 */
public class TomcatReloadValve extends ValveBase {
 
	private static final String RELOAD_CONTEXT_URI = "/reloadContext";
 
	@Override
	public void invoke(Request request, Response response) throws IOException,
			ServletException {
 
		Container container = getContainer();
 
		String requestUri = request.getRequestURI();
 
		String reloadUri = request.getContextPath() + RELOAD_CONTEXT_URI;
 
		if (requestUri.startsWith(reloadUri)
				&& container instanceof Context) {
			reloadContext(response, container);
			return;
		}
 
		getNext().invoke(request, response);
	}
 
	private void reloadContext(Response response, Container container)
			throws IOException {
		((Context) container).reload();
		HttpServletResponse httpResponse = response.getResponse();
 
		httpResponse.setContentType("text/plain;charset=utf-8");
		httpResponse.getWriter().write("Context Reloaded!!");
		httpResponse.getWriter().close();
 
		return;
	}
 
}

 

Posted by 1010
반응형

While on Tomcat 6 and Eclipse Ganymede I found out the following chain to work like charm:

1 stop server

2 project -> clean

3 project build (I had automatic build disabled)

4 delete server

5 delete Servers folder

6 restart Eclipse

7 create new server, add project and start :)

Posted by 1010
반응형

eclipse에서 maven 최신 버전 (0.12.xx)를 사용할 경우 maven build시 아래와 같은 에러 메세지를 확인 할 수 있습니다.


Missing artifact javax.activation:activation:jar:1.0.2:provided 
Missing artifact javax.mail:mail:jar:1.3.1:provided 
Missing artifact javax.xml:namespace:jar:1.1.0:provided


이 메세지가 발생 할 경우 eclipse의 maven intergration을 uninstall 후 하위 버전(0.10.xx)으로 설치해주면 해결 됩니다.

0.12.xx에서 새로 생긴 HttpClient 관련 인증 문제 때문에 발생하는 문제인데 아직까지 해결을 안해주고 있네요 -_-;;

# Appendix

- 위의 방식으로도 계속 Missing Artifact ... 가 발생한다면 아래를 참고하여 해결하시길 바랍니다.

1. right click on your project > Maven > Update Dependencies

then

2. right click on your project > Maven > Update Project configuration

Assuming you M2Eclipse plugin is installed correctly this should solve the issue. Also, check if there is an option right click project > Maven > 3. Enable dependency Management select that

참고 URL : http://stackoverflow.com/questions/5500534/m2eclipse-says-missing-artifact-but-i-can-build-from-cmdline



출처 : http://blog.daum.net/gildongmoo2/15971083

Posted by 1010
반응형

Scheduling 서비스

개요

Scheduling 서비스는 어플리케이션 서버 내에서 주기적으로 발생하거나 반복적으로 발생하는 작업을 지원하는 기능으로서 유닉스의 크론(Cron) 명령어와 유사한 기능을 제공한다.
실행환경 Scheduling 서비스는 오픈소스 소프트웨어로 Quartz 스케쥴러를 사용한다. 본 장에서는 Quartz 스케쥴러의 기본 개념을 살펴본 후, IoC 서비스를 제공하는 Spring과 Quartz 스케쥴러를 통합하여 사용하는 방법을 살펴본다.

설명

Quartz 스케쥴러

Quartz 스케쥴러 실행과 관계된 주요 요소는 Scheduler, Job, JobDetail, Trigger 가 있다.

  • Scheduler 는 Quartz 실행 환경을 관리하는 핵심 개체이다.
  • Job 은 사용자가 수행할 작업을 정의하는 인터페이스로서 Trigger 개체를 이용하여 스케쥴할 수 있다.
  • JobDetail 는 작업명과 작업그룹과 같은 수행할 Job에 대한 상세 정보를 정의하는 개체이다.
  • Trigger 는 정의한 Job 개체의 실행 스케쥴을 정의하는 개체로서 Scheduler 개체에게 Job 수행시점을 알려주는 개체이다.

Quartz 스케쥴러는 수행 작업을 정의하는 Job과 실행 스케쥴을 정의하는 Trigger를 분리함으로써 유연성을 제공한다. Job 과 실행 스케쥴을 정의한 경우, Job은 그대로 두고 실행 스케쥴만을 변경할 수 있다. 또한 하나의 Job에 여러 개의 실행 스케쥴을 정의할 수 있다.

Quartz 스케쥴러 사용 예제

Quartz 스케쥴러의 이해를 돕기 위해 간단한 예제를 살펴본다. 다음 예는 Quartz 매뉴얼에서 참조한 것으로 Quartz를 사용하는 방법과 사용자 Job을 설정하는 방법을 보여준다.

사용자 정의 Job

사용자는 Job 개체를 생성하기 위해 org.quartz.Job 인터페이스를 구현하고 심각한 오류가 발생한 경우 JobExecutionException 예외를 던질 수 있다. Job 인터페이스는 단일 메소드로 execute()을 정의한다.

 public class DumbJob implements Job {
    public void execute(JobExecutionContext context)
      throws JobExecutionException
    {
      System.out.println("DumbJob is executing.");
    }
  }
  • DumbJob은 Job 인터페이스의 execute() 메소드를 구현한다.
  • execute() 메소드는 단순히 Job이 수행됨을 표시하는 메시지를 출력한다.
Quartz 사용 코드
  JobDetail jobDetail = 
            new JobDetail("myJob",// Job 명
              sched.DEFAULT_GROUP,  // Job 그룹명('null' 값인 경우 DEFAULT_GROUP 으로 정의됨)
              DumbJob.class);       // 실행할 Job 클래스
 
  Trigger trigger = TriggerUtils.makeDailyTrigger(8, 30);  // 매일 08시 30분 실행
  trigger.setStartTime(new Date()); // 즉시 시작
  trigger.setName("myTrigger");
 
  sched.scheduleJob(jobDetail, trigger);
  • 우선 Job 설정을 위해 JobDetail 클래스를 정의한다.
  • TriggerUtils을 이용하여 매일 8시30분 실행하는 Trigger를 생성한다.
  • 마지막으로, Scheduler에 JobDetail과 Trigger를 등록한다.

Spring 과 Quartz 통합

Spring은 Scheduling 지원을 위한 통합 클래스를 제공한다. Spring 2.5 는 JDK 1.3 버전부터 포함된 Timer 와 오픈소스 소프트웨어인 Quartz 스케쥴러를 지원한다. 여기서는 Quartz 스케쥴러와 Spring을 통합하여 사용하는 방법을 살펴본다. 
Quartz 스케쥴러와의 통합을 위해 Spring은 Spring 컨텍스트 내에서 Quart Scheduler와 JobDetail, Trigger 를 빈으로 설정할 수 있도록 지원한다. 다음은 예제를 중심으로 Quartz 작업 생성과 작업 스케쥴링, 작업 시작 방법을 살펴본다.

작업 생성

Spring은 작업 생성을 위한 방법으로 다음 두 가지 방식을 제공한다.

  • JobDetailBean을 이용한 방법으로, QuartzJobBean을 상속받아 Job 클래스를 생성하는 방법
  • MethodInvokingJobDetailFactoryBean을 이용하여 Bean 객체의 메소드를 직접 호출하는 방법
JobDetailBean을 이용한 작업 생성

JobDetail는 작업 실행에 필요한 정보를 담고 있는 객체이다. Spring은 JobDetail 빈 생성을 위해 JobDetailBean을 제공한다. 예를 들면 다음과 같다.

JobDetailBean 소스 코드

package egovframework.rte.fdl.scheduling.sample;
 
public class SayHelloJob extends QuartzJobBean {
 
	private String name;
 
	public void setName (String name) {
		this.name = name;	
	}
 
	@Override
	protected void executeInternal (JobExecutionContext ctx) throws JobExecutionException {
		System.out.println("Hello, " + name);
	}
}
  • SayHelloJob 클래스는 작업 생성을 위해 QuartzJobBean의 executeInternal(..) 함수를 오버라이드한다.

JobDetailBean 설정

 <bean id="jobDetailBean"
	class="org.springframework.scheduling.quartz.JobDetailBean">
	<property name="jobClass" value="egovframework.rte.fdl.scheduling.sample.SayHelloJob" />
	<property name="jobDataAsMap">
		<map>
			<entry key="name" value="JobDetail"/>
		</map>
	</property>
  </bean>
  • jobDataAsMap 개체를 이용하여 JobDetail 개체에 Job 설정에 필요한 속성 정보를 전달한다.
MethodInvokingJobDetailFactoryBean을 이용한 작업 생성

소스 코드

package egovframework.rte.fdl.scheduling.sample;
 
public class SayHelloService {
 
	private String name;
 
	public void setName (String name) {
		this.name = name;	
	}
 
	public void sayHello () {
		System.out.println("Hello, " + this.name);
	}
}
  • 작업 수행을 할 Bean 클래스를 정의한다.

설정

<bean id="sayHelloService" class="egovframework.rte.fdl.scheduling.sample.SayHelloService">
	<property name="name" value="FactoryBean"/>
</bean>
 
<bean id="jobDetailFactoryBean"
	class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
	<property name="targetObject" ref="sayHelloService" />
	<property name="targetMethod" value="sayHello" />
	<property name="concurrent" value="false" />
</bean>
  • 정의한 Bean 객체의 메소드를 직접 호출하는 작업을 생성하기 위해 MethodInvokingJobDetailFactoryBean을 정의한다.

작업 스케쥴링

Spring에서 주로 사용되는 Trigger타입은 SimpleTriggerBean과 CronTriggerBean 이 있다. SimpleTrigger 는 특정 시간, 반복 회수, 대기 시간과 같은 단순 스케쥴링에 사용된다. CronTrigger 는 유닉스의 Cron 명령어와 유사하며, 복잡한 스케쥴링에 사용된다. CronTrigger 는 달력을 이용하듯 특정 시간, 요일, 월에 Job 을 수행하도록 설정할 수 있다. 다음은 SimpleTriggerBean과 CronTriggerBean을 이용하여 앞서 생성한 작업을 스케쥴링하는 방법을 살펴본다.

SimpleTriggerBean을 이용한 설정

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
	<property name="jobDetail" ref="jobDetailBean" />
        <!-- 즉시 시작 -->
	<property name="startDelay" value="0" />
   	<!-- 매 10초마다 실행 -->
	<property name="repeatInterval" value="10000" />
</bean>
  • 앞서 JobDetailBean 을 이용하여 생성한 작업을 스케쥴링을 위한 Trigger 에 등록한다. SimpleTriggerBean은 즉시 시작하고 매 10초마다 실행하도록 설정하였다.

CronTriggerBean을 이용한 설정

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
   	<property name="jobDetail" ref="jobDetailFactoryBean" />
   	<!-- 매 10초마다 실행 -->
   	<property name="cronExpression" value="*/10 * * * * ?" />
</bean>
  • 앞서 MethodInvokingJobDetailFactoryBean 을 이용하여 생성한 작업을 스케쥴링을 위한 Trigger 에 등록한다. CronTriggerBean은 매 10초마다 실행하도록 설정하였다. 크론 표현식에 대한 자세한 설명은Quartz Cron 표현식를 참조한다.

작업 시작하기

스케쥴링한 작업의 시작을 위해 Spring 은 SchedulerFactoryBean을 제공한다.

설정

<bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
	<property name="triggers">
		<list>
			<ref bean="simpleTrigger" />
			<ref bean="cronTrigger" />
		</list>
	</property>
</bean>
  • SchedulerFactoryBean 을 이용하여 SimpleTriggerBean 과 CronTriggerBean 기반의 각 Trigger 작업을 시작한다.

참고자료



링크 : http://javastore.tistory.com/96 

링크 : http://kamsi76.egloos.com/470846 

링크 : http://briansjavablog.blogspot.kr/2012/09/spring-quartz-tutorial.html 

Posted by 1010
반응형

The error I get is as follows:

SEVERE: StandardWrapper.Throwable
org.springframework.beans.factory.CannotLoadBeanClassException: Error loading class [org.springframework.scheduling.quartz.JobDetailBean] for bean with name ‘runMeJob’ defined in ServletContext resource [/WEB-INF/quartz-servlet.xml]: problem with class file or dependent class; nested exception is java.lang.IncompatibleClassChangeError: class org.springframework.scheduling.quartz.JobDetailBean has interface org.quartz.JobDetail as super class

 

이런 오류 메시지가 나올때는

 

quartz 다운로드 페이지에서 Quartz 1.8.6 버전으로 lib 교체해주면 된다.



출처 - http://jhroom.tistory.com/129



===================================================================================


WAS를 이용해서 주기적으로 특정 프로그램 실행하는 방법이 spring에서는 2가지가 있다. (뭐 더 있을수도 있다 -_-)

하나는 spring batch이고 다른 하나는 quartz를 이용하는 방법이다.

뭐가좋은지는 나도 잘 모른다. 일단 quartz가 어떤식으로 동작하는지 먼저 공부했으므로 이것부터 글 적는다.

 

1. 샘플 프로젝트 생성

STS 에서 [File] - [New] - [Spring Template Project] 메뉴를 클릭한 후 Spring MVC Project를 이용해 샘플 프로젝트를 생성한다.

 

2. pom.xml 파일 수정

quartz 관련 모듈을 이용하기 위해서 pom.xml 파일에 관련 라이브러리들 넣는다.

spring-tx, quartz, commons-collections, javax.transaction 4개 넣어줘야한다.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${org.springframework-version}</version>
</dependency>
<!-- Quartz framework and dependencies -->
<dependency>
    <groupId>opensymphony</groupId>
    <artifactId>quartz</artifactId>
    <version>1.6.3</version>
    <scope>compile</scope>
</dependency>
<!-- Quartz 1.6.0 depends on commons collections -->
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.1</version>
    <scope>runtime</scope>
</dependency>
<!-- Quartz 1.6.0 requires JTA in non J2EE environments -->
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>jta</artifactId>
    <version>1.1</version>
    <scope>runtime</scope>
</dependency>

 

3. 주기적으로 실행 될 Bean 생성

2개의 Bean을 만든다.

CronQuartz1.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz1 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 1 (5 second): current time = " + sdf.format(time));
    }
 
}

CronQuartz2.java

package com.mungchung.sample;
 
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
 
public class CronQuartz2 extends QuartzJobBean{
    @Override
    protected void executeInternal(JobExecutionContext arg0)
            throws JobExecutionException {
        long time = System.currentTimeMillis();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        System.out.println("Cron trigger 2 (1 minute): current time = " + sdf.format(time));
    }
}

 

4. quartz Bean과 위에서 생성한 Bean 연동

2개의 Bean이 각각 다른 시간차이로 실행되도록 설정해줄것이다.

CronQuartz1.java - 5초마다 실행

CronQuartz2.java - 1분마다 실행

 

/src/main/webapp/WEB-INF/spring/root-context.xml

<!-- 1. Cron 대상이 되는 클래스 정의 -->
<bean id="cronQuartz1" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz1"/>
</bean>
<bean id="cronQuartz2" class="org.springframework.scheduling.quartz.JobDetailBean">
    <property name="jobClass" value="com.mungchung.sample.CronQuartz2"/>
</bean>
 
<!-- 2. Cron 시간 설정 -->
<bean id="cronTrigger1" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz1"/>
    <property name="cronExpression" value="0/5 * * * * ?"/>
</bean>
<bean id="cronTrigger2" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="cronQuartz2"/>
    <property name="cronExpression" value="0 0/1 * * * ?"/>
</bean>
 
<!-- 3. Cron 실행 -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="triggers">
        <list>
            <ref bean="cronTrigger1"/>
            <ref bean="cronTrigger2"/>
        </list>
    </property>
    <property name="quartzProperties">
        <props>
            <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
            <prop key="org.quartz.threadPool.threadCount">3</prop>
            <prop key="org.quartz.threadPool.threadPriority">4</prop>
            <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
            <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>
        </props>
    </property>
</bean>

 

 

Cron Expression

총 7개의 필드 있고 마지막 필드(년도)는 생략 가능하다

 필드이름허용 값 
초(Seconds)0 ~ 59 
분(Minutes)0 ~ 59 
시간(Hours)0 ~ 23
달의 날짜(Day-of-month)1 ~ 31
달(Month) 1 ~ 12 or JAN ~ DEC
주의 날짜(Day-of-week)1 ~ 7 or SUN-SAT
년도(Year) (선택가능) 빈값, 1970 ~ 2099

Cron Expression의 특수문자
Expression설명 예시 
    * 모든 수를 나타냄  
     -값의 사이를 의미* 10-13 * * * *     10,11,12,13분에 동작함 
     ,특정값 지칭* 10,11,13 * * * *      10,11,13분에 동작함
     /값의 증가를 표현* 0/5 * * * *       0분부터 시작해서 5분마다 동작 
     ?특별한 값이 없음을 나타냄(day-of-month, day-of-week 필드만 사용) 
     L마지막 날을 나타냄(day-of-month, day-of-week 필드만 사용) 

6. 실행결과

실행결과를 보면 하나는 5초마다, 다른 하나는 1분마다 Bean이 실행되고 있음을 알수 있다.

01.png





===================================================================================


sping mvc template project로 구성 시

quartz-1.8.6과 sprint-tx를 적용하여 문제 없이 구현 하였다.


spring-tx를 pom.xml에 추가 하지 않을 경우 다음과 같이 에러 발생

심각: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactory' defined in ServletContext resource [/WEB-INF/applicationContext.xml]: Instantiation of bean failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/transaction/TransactionException


===================================================================================


What Is Quartz

   

원문http://www.onjava.com/lpt/a/6207

저자Chuck Cavaness

2005-09-28

   

Quartz

Quartz는 오픈 소스 작업 스케줄링 프레임워크이다. Quartz는 완전히 자바로 작성되어 있으며, J2SE와 J2EE 어플리케이션 모두에서 사용될 목적으로 설계되었다. Quartz는 매우 유연하며 단순한 구조를 제공한다. 간단한 작업은 물론 복잡한 작업 모두에 대한 스케줄링을 작성할 수 있다. Quartz는 또한 EJB, JavaMail 등을 위한 데이터베이스 지원, 클러스터링, 플러그 인, 미리 내장된 작업들을 포함하고 있으며, cron과 유사한 표현식도 지원한다.

   

매일 또는 매주 오후 11시 30분 또는 매달 말일에만 실행하는 작업을 수행하는 어플리케이션을 작성해 본 적이 있는가? 수작업 없이 자동으로 실행될 수 있는 작업이 실행되는 동안 만약 실행하는 동안 심각한 오류가 발생할 경우, 어플리케이션은 잘못되었다는 것을 스스로 알아내 알려주고 이를 다시 실행시키도록 시도해야 할 것인가? 여러분과 팀이 자바로 프로그램을 작성하고 있는가? 만약 이들 질문들에 대한 대답이 "그렇다"라면, Quartz Scheduler를 사용해 보아라.

   

Job Scheduling Made Easy

Quartz는 오픈 소스 작업 스케줄링 프레임워크로 완전히 자바로 작성되었다. 작업 스케줄링 이라는 용어에 대해 너무 겁먹지 말아라. Quartz 프레임워크는 수많은 기능들이 매우 단순한 형태로 무장되어 있으며, 놀라울 정도로 매우 사용하기 쉽게 되어 있다.

   

org.quartz.Job 인터페이스를 구현하는 자바 클래스를 작성하기만 하면 된다. Job 인터페이스는 다음과 같은 하나의 메소드만을 포함하고 있다.

   

public void execute(JobExecutionContext context) throws JobExecutionException;

   

여러분이 작성한 Job 클래스에서 execute() 메소드에 몇 가지 로직을 추가한다. Job 클래스와 스케줄을 설정하게 되면, Quartz는 그 나머지 작업을 처리해준다. Scheduler가 Job에 알려줄 시간이라고 판단하게 되면, Quartz 프레임워크는 Job 클래스의 execute() 메소드를 실행하여 작업을 수행하도록 만들어준다. Scheduler에 어떠한 것도 보고해줄 필요가 없으며, 어떤 특별한 메소드를 호출할 필요도 없다. 단순히 Job 내에 있는 작업들만 수행해주면 끝이다. 만약 여러분이 이후에 다시 Job이 호출되도록 설정했다면, Quartz 프레임워크는 적절한 때에 다시 이를 호출하는 부분을 담당해준다.

   

만약 여러분이 Apache Struts와 같은 유명한 오픈 소스 프레임워크를 써본 경험이 있다면, Quartz의 설계 내용과 컴포넌트에도 쉽게 익숙해질 것이다. 비록 이 두 오픈 소스 프로젝트가 전혀 서로 다른 문제점들을 해결해주고 있지만, 오픈 소스 소프트웨어를 항시 사용하는 사람들이라면 편안한 느낌을 받을 것이다. Quartz는 표준 독립형 J2SE 어플리케이션 내에서라든지, 웹 어플리케이션 내부, 심지어는 J2EE 어플리케이션 서버 내에서 사용될 수 있다.

   

The History behind Quartz

Quartz가 주목을 받기 시작한 것은 올해부터지만, 나온 지는 좀 되었다. Quartz는 James House에 의해 개발되었으며, 원래는2001년 봄에 SourceForge 프로젝트에 추가되었다. 수년이 지나면서 많은 기능들이 추가되어 배포되었지만, 두각을 나타내기 시작하면서 주목을 받기 시작한 것은 OpenSymphony 프로젝트의 일부로 되면서 새로운 사이트로 옮겨지게 된 때부터였다.

   

House는 자신을 도와주는 여러 파트 타임 개발자와 함께 여전히 개발 작업의 많은 부분에 참여하고 있다. Quartz 개발 팀은 올 해 1.5 배포판을 포함해 다양한 새로운 버전들을 배포할 수 있었으며, 이는 현재 candidate 릴리즈 단계에 있다.

   

Getting Your Hands on Quartz

Quartz 프로젝트는 OpenSymphony 사이트에 호스팅 되어 있다. 이 사이트에는 JavaDocs, 튜토리얼, CVS, 사용자와 개발자 포럼 및 다운로드 링크와 같은 여러 유용한 정보들이 제공되고 있다.

   

다운로드 링크에서 배포판을 다운로드 한 후, 적절한 위치에 압축을 푼다. 이 안에 보면 어플리케이션에서 사용할 수 있는 Quartz바이너리 파일이 포함되어 있다. Quartz 프레임워크에서 요구 되는 다른 의존적인 라이브러리는 거의 없다.

   

배포판에 보면, <Quartz 설치 디렉터리>/lib/core와 <Quartz 설치 디렉터리>/lib/optional 디렉터리에 있는 다른 의존 라이브러리들을 프로젝트에 추가하면 된다. 이들 대부분은 표준 Jakarta Commons 라이브러리들로, 여러분도 잘 알고 있는 Commons Logging, Commons BeanUtils 등이다.

   

The quartz.properties File

Quartz에는 quartz.properties라는 환경 설정 파일이 포함되어 있다. 이 파일을 사용하면 Quartz 프레임워크의 런타임 환경을 수정할 수 있다. 디폴트로, Quartz 바이너리 내에 포함되어 있는 파일이 사용된다. 이 파일을 복사하여 클래스들이 있는 디렉터리에 두면 클래스 로더가 이를 참조할 수 있다. 예제 1은 quartz.properties 파일에 대한 간단한 예이다.

   

예제 1. quartz.properties 파일을 통해 Quartz 런타임을 변경할 수 있다.

#===============================================================

# Configure Main Scheduler Properties

#===============================================================

   

org.quartz.scheduler.instanceName = QuartzScheduler

org.quartz.scheduler.instanceId = AUTO

   

#===============================================================

# Configure ThreadPool

#===============================================================

   

org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool

org.quartz.threadPool.threadCount = 5

org.quartz.threadPool.threadPriority = 5

   

#===============================================================

# Configure JobStore

#===============================================================

   

org.quartz.jobStore.misfireThreshold = 60000

org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

   

일단 Quartz 바이너리와 의존 라이브러리들을 프로젝트에 추가했고, quartz.properties 파일을 클래스패스 디렉터리에 추가했다면, 이제 몇몇 Job들을 작성해보자. 그러나, 이 작업을 해보기 전에 Quartz 아키텍처에 관해 간단하게 살펴보도록 하자.

   

Inside the Quartz Architecture

크기로 보자면, Quartz는 다른 대부분의 오픈 소스 프레임워크들과 유사하다. Quartz는 약 300 개의 자바 클래스들과 인터페이스들을 포함하고 있으며, 이들은 약 12개의 패키지로 구성되어 있다. 이는 Apache Struts에 있는 11개의 패키지에 약 325개의 클래스들과 인터페이스들에 비견될 수 있다. 비록 이 크기가 프레임워크의 질을 결정하는데 사용되는 특징이 될 수는 없겠지만, 여기에서 말하고자 하는 점은 Quartz 내에 수많은 기능들이 포함되어 있다는 사실이다. 그리고, 그러한 기능들이 바로 프레임워크의 질을 결정하는데 사용되는 하나의 요소이다. 이는 오픈 소스에만 국한되는 내용은 아니다.

   

The Quartz Scheduler

Quartz 프레임워크의 핵심은 Scheduler 이다. Scheduler는 Quartz 어플리케이션을 위한 런타임 환경을 관리하는 책임을 지니고 있다. Scheduler 그 자체가 모든 작업들 수행하는 것은 아니다. Scheduler는 프레임워크 내부에 있는 매우 중요한 몇몇 컴포넌트들에 그 작업을 의존하고 있다. 확장성을 보장하기 위해, Quartz는 멀티스레드 아키텍처를 기반으로 하고 있다. Quartz 프레임워크가 시작될 때, 프레임워크는 스케줄링 된 Job들을 실행하기 위해 Scheduler가 사용하는 "worker 스레드들"을 초기화 한다. Quartz가 동시에 수많은 Job들을 실행 가능한 이유가 바로 여기에 있다. Quartz는 스레드 환경을 관리하기 위해 ThreadPool관리 컴포넌트들에 의존하고 있는데, 그 결합도는 느슨하다. 이 글을 통해 여러 번 언급할 것이지만, 이는 Quartz에 있는 모든 것들은 환경 설정이 가능하거나 사용자가 정의해 지정할 수 있음을 의미한다. 예를 들면, 여러분이 정의한 "ThreadPool" 관리 기능을 플러그 인 형태로 끼워 넣고 싶은 경우, 이러한 작업이 가능하다.

   

Jobs, Jobs, and More Jobs

Quartz의 용어를 사용하자면, Job은 작업을 수행하는 간단한 자바 클래스이다. 이 작업은 자바에서 코드로 작성 가능한 그 어떠한 것이라도 될 수 있다. 필수적으로 요구되는 사항은, org.quartz.Job 인터페이스를 구현하고, 심각한 오류 발생 시JobExecutionException을 발생시키는 것뿐이다. 여러분은 이미 앞에서 Job 인터페이스와 포함된 execute() 메소드를 보았다.

   

Job 인터페이스와 execute() 메소드를 구현했다면, Quartz는 Job 실행 시기가 되었을 때, Job을 호출한다. execute() 메소드 안에서 무엇을 수행하는가는 전적으로 개발자에게 달려 있다. 다음은 Job 내부에서 실행할 작업들에 대한 몇몇 예이다.

   

● JavaMail이나 Commons Net과 같은 다른 메일 프레임워크를 사용하여 전자메일 전송

● EJB에 대한 원격 인터페이스를 생성한 후, 이 인터페이스의 메소드 호출

● Hibernate 세션을 얻어 관계형 데이터베이스에 있는 데이터 질의와 갱신

● OSWorkflow를 사용하여 Job으로부터 워크플로우 호출

● FTP를 사용해 파일 옮기기

● Ant 빌드 스크립트를 호출해 스케줄링 되어 있는 빌드 작업 시작

   

수많은 여러 작업들이 가능하며, 이것이 바로 Quartz 프레임워크를 매우 강력하게 만들어주는 이유이다. Quartz는 매우 일반적이고 반복적인 스케줄을 작성해주는 메커니즘을 제공하기 때문에, 개발자는 단지 실행을 위해 호출될 자바 클래스들만 작성하면 된다.

   

Job Management and Storage

Job들의 스케줄이 지정되었다면, Scheduler는 이러한 Job들을 기억하고 이들을 실행시킬 시간을 지속적으로 추적해야 한다. 만약 여러분의 Job이 30분 늦게 시작되거나 30초 일찍 시작된다면 Quartz는 그렇게 유용하지 않을 것이다. 사실, 스케줄이 지정된Job들 상에 있는 execute() 메소드를 호출하는 시간은 매우 정확해야 한다. Job 저장과 관리는 Quartz에서 JobStore로 일컬어지는 개념을 통해 이루어진다.

   

Available JobStores

Quartz 프레임워크에서는 두 가지 기본적인 JobStore 타입을 제공한다. Scheculer 정보를 유지하는데 일반적인 메모리(RAM)을 사용하는 첫 번째 타입은 RAMJobStore라 불린다. 이러한 타입의 JobStore는 설정 및 실행이 매우 간단하다. 많은 어플리케이션들에 대해 이러한 JobStore만으로도 충분할 것이다. 그러나, Scheduler 정보가 JVM에 할당되어 있는 메모리에 저장되기 때문에, 어플리케이션이 멈추게 되면, 스케줄과 관련된 모든 정보가 사라진다. 만약 어플리케이션이 재 시작하는 경우에도 이러한 스케줄 정보를 유지할 필요가 있다면, 두 번째 유형의 JobStore을 사용해야 할 것이다.

   

두 번째 타입의 JobStore는 실제로 Quartz 프레임워크에서 두 가지의 서로 다른 형태로 구현되어 제공되고 있지만, 이 둘 모두 일반적으로 JDBC JobStore로 일컬어지고 있다. 이 두 가지 모두의 JDBC JobStore는 JDBC 드라이버를 사용하며, 스케줄 정보를 유지하고 있는 관계형 데이터베이스로부터 정보를 가져온다. 이 두 가지 타입은 데이터베이스 트랜잭션을 제어하는지의 여부나 BEA의 WebLogic이나 JBoss와 같은 어플리케이션 컨테이너에 제어를 넘기는지의 여부에 그 차이점이 존재한다 (이는J2EE에서의 BMT와 CMT 사이의 차이점과 유사하다).

   

두 가지 유형의 JDBC JobStore는 다음과 같다.

   

● JobStoreTX: 트랜잭션을 제어하고 싶은 경우나, 서버 환경 없이 어플리케이션을 운영하려 할 때 사용된다.

● JobStoreCMT: 어플리케이션 서버 환경 내에서 어플리케이션이 운영되며 컨테이너가 트랜잭션을 관리하도록 하고 싶은 경우 사용된다.

   

JDBC JobStore는 어플리케이션이 중지되고 다시 시작된 후에라도 스케줄링 정보를 유지하여 Scheduler가 실행되도록 만들어야 할 경우를 위해 설계되었다.

   

Job and Triggers

Quartz 설계자들은 Job과 스케줄을 분리하였다. Quartz에서 Trigger는 Job이 트리거링 되거나 발생되어야 할 때, Scheduler에게 알려주는데 사용된다. Quartz 프레임워크에서는 간단한 Trigger 타입들을 제공하고 있는데, SimpleTrigger와 CronTrigger가 가장 일반적으로 사용된다.

   

SimpleTrigger는 스케줄을 간단히 발생시키는데 사용될 목적으로 설계되었다. 일반적으로, 주어진 시간에 Job을 발생시켜 (m)초 간격을 두고 여러 번(n) 이를 실행할 필요가 있을 경우, SimpleTrigger가 적합한 선택이 된다. 반면, Job에 요구되는 스케줄링이 복잡할 경우, CronTrigger가 적합할 것이다.

   

CronTrigger는 달력과 유사한 스케줄에 기반하고 있다. 만약 여러분의 Job이 매주 토요일과 일요일을 제외한, 매일 오전 10시30분마다 실행되어야 하는 경우에, CronTrigger가 사용된다. 이 이름이 암시하고 있듯, CronTrigger는 Unix의 cron 표현식을 기반으로 하고 있다. 예를 들면, 다음의 Quartz cron 표현식은 월요일부터 금요일에 걸쳐 매일 오전 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * MON-FRI

   

그리고 다음 표현식은 2002, 2003, 2004, 2005년 동안 매월 마지막 금요일 오후 10시 15분에 Job을 실행할 것이다.

   

0 15 10 ? * 6L 2002-2005

   

이러한 작업은 SimpleTrigger로  수행할 수 없다. 이 둘 모두 Job에 사용될 수 있다. 어떠한 것을 사용할 지에 관한 선택은 스케줄링 될 작업 성격에 달려 있다.

   

Scheduling a Job

이제 예제 Job을 살펴보면서 실제 사용에 관한 부분에 대해 토의해보자. 클라이언트가 자신의 FTP 사이트에 파일을 저장할 때마다 부서에 전자 메일로 통지할 필요가 있는 상황을 여러분이 관리하고 있다고 가정해보자. 우리의 Job은 원격 서버에 있는 파일들을 다운로드 하는 것이 된다. 그런 후, Job은 발견된 파일들의 다운로드 횟수를 포함하고 있는 전자 메일을 전송하게 된다. 이Job은 누군가가 하루 동안 이러한 작업을 수작업으로 전송할 필요가 없도록 편리하게 만들어준다. 우리는 이러한 Job이 일주일 내내 하루 24시간 동안 매 60초마다 검사하도록 설정할 수 있다. 이는 바로 Quartz 프레임워크를 완벽하게 사용하는 예이다.

   

첫 번째 단계는 FTP와 Email 로직을 수행하는 Job 클래스를 작성하는 것이다. 다음 예제는 Quartz Job 클래스를 나타낸 것으로, org.quartz.Job 인터페이스를 구현하고 있다.

   

예제 2. FTP 사이트에서 파일들을 다운로드 받고 Email을 전송하는 Quartz Job

   

public class ScanFTPSiteJob implements Job {

    private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);

   

    /*

     * 정확한 시간에 스케줄러 프레임워크에 의해 호출된다.

    */

    public void execute(JobExecutionContext context) throws JobExecutionException {

        JobDataMap jobDataMap = context.getJobDataMap();

   

        try {

            // FTP 사이트에서 파일들 검사

            File[] files = JobUtil.checkForFiles(jobDataMap);

            JobUtil.sendEmail(jobDataMap, files);

        } catch (Exception ex) {

            throw new JobExecutionException(ex.getMessage());

        }

    }

}

   

이 글에서는 일부러 ScanFTPSiteJob을 매우 간단하게 구성하였다. 또한 이 글에서는 이 예제를 위해 JobUtil이라는 유틸리티 클래스를 작성하였다. 이 클래스는 Quartz의 부분이 아니지만, 다양한 Job들에서 재사용 할 수 있는 여러분만의 유틸리티 성격의 라이브러리를 만드는 것이 좋다. 이 글에서는 Job 클래스와 Quartz Scheduler 내부에 모든 코드들을 쉽게 둘 수도 있었지만, Quartz를 사용하는 것 때문에 재사용을 고려하지 않을 수는 없었다.

   

JobUtil.checkForFiles()와 JobUtil.sendEmail()이 사용하는 파라미터들은 JobDataMap 객체를 사용하고 있는데, 이 객체는Quartz가 생성한 객체이다. 이 인스턴스는 Job이 실행될 때마다 생성되며, 이를 사용해 Job 클래스로 환경 설정 파라미터들을 넘겨준다.

   

JobUtil의 구현 부분은 여기에 나타내지 않았지만, 우리는 FTP와 Email 기능을 모두 구현하고 있는 Jakarta의 Commons Net을 통해 매우 쉽게 사용할 수 있었다.

   

Calling Your Jobs with the Scheduler

Job을 생성하는 것이 첫 번째 작업이지만, Scheduler에 의해 Job이 호출되도록 하기 위해서는 Scheduler에게 얼마나 자주, 언제 Job이 호출되어야 하는지 알려주어야 한다. 이 작업은 Trigger를 Job에 연관시킴으로써 이루어진다. 우리는 Scheduler가 계속 매 60초마다 Job을 호출하는데 관심을 두고 있기 때문에, SimpleTrigger를 사용할 것이다.

   

Job과 Trigger의 스케줄은 Quartz Scheduler 인터페이스를 통해 이루어진다. Scheduler의 인스턴스를 얻기 위해서는 팩토리로부터 인스턴스를 얻어와야 한다. 이를 위한 가장 쉬운 방법은 StdSchedulerFactory 클래스의 static 메소드인getDefaultScheduler()를 호출하는 것이다.

   

Quartz 프레임워크를 사용할 때, 반드시 start() 메소드를 호출하여 Scheduler를 시작시켜야 한다. 예제 3에 있는 코드는 대부분의 Quartz 어플리케이션의 일반적인 패턴을 따르고 있다. 대부분의 Quartz 어플리케이션에서는, 하나 또는 그 이상의 Job들을 생성하고 Trigger들을 생성하고 설정한 후, Scheduler에 Job과 Trigger들에 대한 스케줄을 정하고 Scheduler를 시작시킨다(주: Scheduler를 먼저 시작시켜도 된다. 이는 중요하지 않다).

   

예제 3. Quartz Job들은 Quartz Scheduler를 통해 스케줄이 지정되어야 한다.

   

public class MyQuartzServer {

    public static void main(String[] args) {

        MyQuartzServer server = new MyQuartzServer();

   

        try {

            server.startScheduler();

        } catch(SchedulerException ex) {

            ex.printStackTrack();

        }

    }

   

    protected void startScheduler() throws SchedulerException {

        // 팩토리를 사용해 Scheduler 인스턴스를 생성

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

   

        // JobDetail은 Job들에 대한 정의를 포함한다.

        JobDetail jobDetail = new JobDetail("ScanFTPJob", Scheduler.DEFAULT_GROUP,

                                          ScanFTPSiteJob.class);

        // execute() 내에서 사용될 job 파라미터들을 저장

        jobDetails.getJobDataMap().put("FTP_HOST", "\home\cavaness\inbound");

   

        // 여기에 Job 파라미터들에 필요한 기타 다른 내용들이 온다.

   

        // 매 60초마다 발생하는 Trigger 인스턴스 생성

        Trigger trigger = TriggerUtils.makeSecondlyTrigger(60);

   

        // Scheduler에 Job과 Trigger를 설정

        scheduler.scheduleJob(jobDetail, trigger);

   

        // Scheduler 실행 시작

        scheduler.start();

    }

}

   

Programmatic vs. Declarative Scheduling

예제 3에서, 우리는 프로그래밍을 통해 ScanFTPSiteJob 스케줄을 작성했다. 즉, Scheduler에 Job과 Trigger를 설정하기 위해 자바 코드를 사용하였다는 의미이다. Quartz 프레임워크에서는 XML 파일들에 Job 스케줄을 선언적으로 설정할 수 있는 기능을 지원하고 있다. 선언적인 접근 방법을 통해 어떠한 Job이 언제 실행되어야 하는지를 보다 빠르게 지정할 수 있다.

   

Quartz 프레임워크에는 Quartz 어플리케이션이 시작하자마자, Job과 Trigger 정보를 포함하고 있는 XML 파일을 읽어 들이는"플러그 인"을 포함하고 있다. XML 내에 있는 모든 Job들은 이들과 관련된 Trigger들과 함께 Scheduler에 추가된다. 물론 Job클래스들을 작성해야 하지만, 그러한 Job들을 갖는 Scheduler의 환경을 설정하는 것은 매우 동적으로 이루어진다. 예제 4는 예제 3의 코드에 있는 동일한 로직을 수행하는데, 선언적인 방법으로 구성되어 있다.

   

예제 4. Quartz Job들은 XML 파일을 사용하여 스케줄링 될 수 있다.

   

<?xml version="1.0" encoding="utf-8"?>

<quartz>

    <job>

        <job-detail>

            <name>ScanFTPSiteJob</name>

            <group>DEFAULT</group>

            <description>A job that scans an ftp site for files</description>

            <job-class>ScanFTPSiteJob</job-class>

   

            <job-data-map allows-transient-data="true">

                <entry>

                    <key>FTP_HOST</key>

                    <value>homecavanessinbound</value>

                </entry>

   

                <!-- 다른 필요한 Job 파라미터들을 여기에 둔다 -->

            </job-data-map>

        </job-detail>

   

        <trigger>

            <simple>

                <name>ScanFTPSiteJobTrigger</name>

                <group>DEFAULT</group>

                <job-name>ScanFTPSiteJob</job-name>

                <job-group>DEFAULT</job-group>

                <start-time>2005-09-11 6:10:00 PM</start-time>

                <!-- 계속 60초마다 반복 실행 -->

                <repeat-count>-1</repeat-count>

                <repeat-interval>60000</repeat-interval>

            </simple>

        </trigger>

    </job>

</quartz>

   

예제 4에 있는 XML 엘리먼트들을 예제 3에 있는 자바 코드와 비교해볼 수도 있다. 이들은 개념적으로 동일하다. 예제 4에 보여지는 것과 같은 선언적인 접근 방법의 장점은 유지보수가 매우 간단해진다는 것이다. XML 파일만 변경하고 Quartz 어플리케이션을 재 시작 하기만 하면 되기 때문이다. 소스 코드를 수정하고 재 컴파일 하여 배포할 필요가 없다.

   

Stateful and Stateless Jobs

이 글에서 살펴보았던 Quartz Job 예제는 모두 상태 정보를 가지고 있지 않다. 즉, 각각의 Job이 실행에 대해, Job이 실행되는 동안 JobDataMap에 가해진 어떠한 변경 사항들도 유지되지 않는다는 것을 의미한다. 만약 JobDataMap에 값을 추가, 변경 또는 삭제하는 기능이 필요하며, 다음 실행에 이러한 변경 사항들이 Job에 반영되도록 해야 한다면, Quartz Stateful Job이 필요하다.

   

만약 여러분이 EJB 개발을 경험해 보았다면, Stateful이라는 것이 부정적인 의미를 담고 있다는 점 때문에 여러분은 지금 움찔하고 있을 것이다. 이는 주로 "Stateful EJB"가 가지고 있는 확장성 이슈로부터 기인한다. Quartz Stateful Job은org.quartz.StatefulJob 인터페이스를 통해 구현된다. Stateless Job과 Stateful Job 간의 주요 차이점은, Stateful Job은 한 번에Job을 실행하는 인스턴스가 오직 하나를 가질 수 있다는 점이다. 그러므로, 예제 3의 경우, "ScanFTPJob" Job을 실행하는 인스턴스는 한 번에 하나만 가지게 된다. 대부분의 경우, 이는 커다란 문제점을 나타내지는 않는다. 그러나, 만약 자주 실행될 필요가 있는 Job을 가지고 있다거나, 작업 완료까지 오랜 시간을 필요로 하는 Job을 가지고 있을 경우, Stateful Quartz Job은 확장성에 문제를 가져다 줄 수 있다.

   

Other Features of the Quartz Framework

Quartz 프레임워크는 매우 다양한 기능들을 가지고 있다. 사실, 한 번에 이 모든 기능들을 나열하기에는 너무나 많다. 다음 목록은 이 글에서는 자세히 언급할 시간이 없는 Quartz 내의 여러 기능들 중 몇몇을 간단하게나마 설명한 것이다.

   

Listeners and Plugins

오늘날 어떠한 오픈 소스 프레임워크라도 사용하고 있는 개념이 바로 이 둘이다.

   

Quartz Listener는 주요 이벤트가 발생할 때, 프레임워크 내부로부터 콜백을 받는 자바 클래스이다. 예를 들면, Job이 스케줄 되어 있거나 스케줄 되어 있지 않을 때, 또는 Trigger가 끝났거나, 더 이상 발생시키지 않을 때, 이러한 모든 것들은 Listener로 통지되도록 설정될 수 있다. Quartz 프레임워크는 Scheduler, Job, Trigger들을 위한 Listener들을 포함하고 있다. 또한, Job Listener와 Trigger Listener들을 특정한 한 Job이나 Trigger에 적용되도록 만들거나 전체에 걸쳐 적용되도록 설정할 수도 있다.

   

일단 Listener가 호출되면, 이 정보를 사용해 Listener 클래스 내에서 수행하려는 어떠한 로직이라도 실행시킬 수 있다. 예를 들면, 만약 Job이 완료될 때마다 전자 메일을 보내고 싶은 경우, 이를 Job에 프로그래밍 해 넣을 수 있다. 또한, JobListener를 사용할 수도 있다. 이 JobListener는 결합도를 느슨하게 만들어 보다 나은 설계를 만들어주는데 도움을 줄 수도 있다.

   

Quartz Plugin은 Quartz 소스를 수정하지 않고도 Quartz 프레임워크에 기능을 추가시켜주는 새로운 기능이다. 이것은 Quartz프레임워크를 확장해야 하는데, 변경한 기능을 Quartz 개발 팀에게 보내주고 다음 버전에 반영되기까지 기다릴 시간이 없는 개발자들을 위한 기능이다. 여러분이 Struts 플러그 인에 익숙하다면, Quartz 플러그 인을 사용하는 방법을 더 쉽게 이해할 수 있을 것이다.

   

Clustering Quartz Applications

Quartz 어플리케이션은 여러분의 요구 사항에 따라 수직/수평적으로 모두 클러스터링 될 수 있다. 클리스터링을 통해 다른 클러스터링 타입과 마찬가지의 이점들을 제공받을 수 있다.

   

● 확장성

● 높은 가용성

● 로드 밸런싱

   

현재 Quartz는 관계형 데이터베이스와 JDBC JobStore중 하나의 도움을 통해 클러스터링을 지원한다. 향후 버전에서는, 이러한 제한이 사라져, 데이터베이스 없이도 RAMJobStore에서 사용 가능해질 것이다.

   

The Quartz Web Application

Quartz 프레임워크를 2-3주나 몇 달간 사용한 후 보통 Quartz 사용자들이 가장 많이 요구하는 것들 중 하나는 Quartz를 GUI에 통합하는 것에 대한 것이다. 자바 서블릿을 사용하여 Quartz를 초기화 하고 시작시킬 수 있는 기능이 현재 프레임워크에 제공되고 있다. 일단 여러분이 Scheduler 인스턴스에 접근하게 되면, 이 인스턴스는 웹 컨테이너의 ServletContext에 저장하고Scheduler 인터페이스를 통해 스케줄링 환경을 관리할 수 있다.

   

다행히도, 몇몇 Quartz 개발자들은 Quartz Scheduler 환경을 보다 잘 관리하는데 사용될 수 있는 독립형 Quartz Web Application에 대해 작업해 오고 있다. Struts와 Spring과 같은 다수의 유명한 오픈 소스 프로젝트를 기반으로 구축되어 이 GUI는 많은 기능들을 지원하는데, 이들은 간단한 인터페이스에 랩핑 되어 있다. 그림 1은 이 GUI를 잡아낸 것이다.

   

   

그림 1. Quartz 환경을 보다 쉽게 관리해주는데 도움을 주는 Quartz Web Application

   

What's down the Road?

이미 다음 주요 릴리즈에 대한 움직임도 진행되고 있을 정도로 Quartz 프로젝트는 활발하게 진행되고 있다. OpenSymphony의wiki에서 Quartz 2.0에서 고려되고 있는 기능들과 설계에 대한 정보를 얻을 수도 있다.

   

항상 그렇듯이, 날마다 Quartz 사용자들은 프레임워크에서 고려될 수 있는 갖가지 기능들에 대한 제안이나 설계에 관해 자유롭게 제안하고 있다.

   

Find Out More about Quartz

Quartz 프레임워크의 보다 많은 기능들을 사용해 나갈수록, User and Developer Forum은 Quartz 사용자들과의 질문/답변 및 커뮤니케이션을 위한 매우 유용한 자원이 될 것이다.

   

출처 : http://blog.empas.com/kkamdung/12297998

[출처] Quartz - java 스케쥴러|작성자 순짱

 

===================================================================================

 

가끔 서버에서 주기적으로 어떠한 작업을 하고자 할때 리눅스에서는 크론탭을 사용하여 주기적으로 어떠한 작업을 처리합니다.
이런 주기적 작업을 처리하기위해 Spring에서 지원해 주는 Quartz스케쥴러를 통해 크론탭과 같은 역할을 하는 스케쥴러를 작성할  있습니다.
이번에는 Spring  Quartz 연동하여 스케줄러를 작성해 보겠습니다.

작업순서는 
스프링 기본 세팅 -> Quartz 세팅 순으로 작업하겠습니다.

1. 
스프링 기본 설정
1) springframework.org 
 이동하셔서 스프링 라이브러리를 다운 받습니다

위와 같은 페이지가 뜨면 해당사항을 입력하시고 Access Download  클릭하셔서 다운로드 페이지로 이동합니다. (귀찮으신 분들은 하단의 파란색으로 "download page" 선택하시면 입력하시지 않고도 다운로드 페이지로 이동하실수 있습니다.

많은 버전의 라이브러리  spring-framework-2.5.6.SEC02.zip  다운 받습니다다른 버전을 다운 받으셔도 상관없습니다만 버전에 따라 세팅 내용이 조금  달라지므로 같은 버전의 라이브러리로 진행하는 것이 나을 것같네요~^^.

2) 
이렇게 라이브러리까지 다운로드 받고 나면 Eclipse 같은 IDE에서 Dynamic Web Project 선택하여 Project 한개 생성합니다.
(
저는 SpringQuartz 라는 이름으로 생성했습니다.)

3) 
프로젝트가 생성되면 프로젝트 안에 /WEB-INF/lib 디렉토리에 스프링 라이브러리를 압축  곳에 있는 dist/spring.jar파일을 추가합니다.
 : 프로젝트를 진행하다 보면 위와같이 라이브러리 버전이 없는 jar파일을 그냥 추가하는 경우가 있는데 나중에 라이브러리를 업데이트 해야 할일이 생기게 되면 위와같이 spring.jar 라고 되어있으면 지금 적용되어 있는 버전이  인지 알수가 없습니다그렇기 때문에 항상 라이브러리 추가하실때는 추가하시는 라이브러리의 버전 번호를 파일이름 뒤에 추가하는 습관 들이 시는게 좋습니다
     ex) spring-2.5.6.jar

4) 
프로젝트 안에 생성된 web.xml Spring 사용하기 위한 세팅을 추가해 줍니다.
저는
 Quartz 사용하기 위한 최소한의 Spring 세팅을 해놓았기 때문에 세팅 내용이 단순합니다만약 웹프로젝트와 함께 Quartz 사용하신다면 웹에 맞게 설정하시고 사용하셔야 함을 알려드립니다.^^

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee" 

xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 

http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>/WEB-INF/config/applicationContext*.xml</param-value>

</context-param>

</web-app>


5) 쿼츠 라이브러리를 다운로드 받고 라이브러리를 추가해 줍니다.
쿼츠 라이브러리 다운로드 하신다음 압축을 풀어 줍니다.
해당 라이브러리를 프로젝트의 lib 디렉토리에 복사하여 넣어줍니다.
- quartz-all-1.8.3.jar
압축푼 lib 디렉터리의 log4j-1.2.14.jar
압축푼 lib 디렉터리의 slf4j-api-1.5.10.jar
압축푼 lib 디렉터리의 slf4j-log4j12-1.5.10.jar
 추가  줍니다.
마지막으로 apache 
commons-logging-1.1.1.jar  다운로드 하셔서 위와 같이 프로젝트의 lib 추가해주시면 라이브러리 추가는 끝이 납니다.

6) Quartz
 핵심적인 기능을  /WEB-INF/config/applicationConext.xml  작성합니다.
스케쥴러의 핵심 세팅은 3가지 정도 입니다.
하나실제 주기적으로 실행될 클래스 등록
스케줄러가 동작하는 interval time 설정
실제 동작하게  설정

이런 세가지가 있겠습니다.

스케줄러 동작방식에는 두가지가 존재 합니다.
-Simple : interval time
 간단하게 동작하는 방식으로 몇초혹은 몇분몇시간 단위로 작동하고 싶을때 사용합니다
<Simple type setting>

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:p="http://www.springframework.org/schema/p"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://www.springframework.org/schema/beans   

                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

                           http://www.springframework.org/schema/context

                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!-- 하나.주기적으로 실행될 클래스 설정 -->

<!-- property name은 jobClass로 fix, value는 사용자가 작성한 class 파일 위치 -->

<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>

</bean>


<!-- 둘.스케줄러의 interval time 설정 -->

<!-- 쿼츠에는 아래와 같이 몇초에 한번씩 돌게 하는 Simple type 과 -->

<!-- 무슨 요일 몇시에 한번씩 돌게 하는 날짜로 지정하는 Cron type 이 있다. -->

<!-- 현재는 Simple type으로 세팅 -->

<!-- jobDetail은 위에서 설정한 실제 동작할 클래스 id를 적어준다 -->

<!-- startDelay는 서버 시작후 몇초 뒤에 시작할지 세팅(ms 단위)  -->

<!-- repeatInterval은 몇 초에 한번씩 실행될 건지 세팅(ms 단위: 현재 1초) -->

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">

<property name="jobDetail" ref="simpleQuartzJob"/>

<property name="startDelay" value="1000"/>

<property name="repeatInterval" value="1000"/>

</bean>

<!--셋. 실제 동작하게끔 설정 -->

<!--ref bean은 위에서 설정한 interval time 아이디를 넣어주면 됨  -->

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<list>

<ref bean="simpleTrigger"/>

</list>

</property>

<!-- Quartz 실행시 세팅 -->

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

     <prop key="org.quartz.threadPool.threadCount">5</prop>

     <prop key="org.quartz.threadPool.threadPriority">4</prop>

     <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

     <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>

</beans>



-Cron : linux  Cron tab  같은 역할을 하는 타입니다 몇월몇일 몇시에 동작하게 하고 싶으면 Cron type 사용하시면 됩니다<?xml version="1.0" encoding="UTF-8" ?>.
<Cron type setting>

<beans xmlns="http://www.springframework.org/schema/beans"

   xmlns:context="http://www.springframework.org/schema/context"

   xmlns:p="http://www.springframework.org/schema/p"

   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

   xsi:schemaLocation="http://www.springframework.org/schema/beans   

                           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd

                           http://www.springframework.org/schema/context

                           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

<!--하나. 주기적으로 실행될 클래스 설정 -->

<bean id="cronQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.CronQuartzJob"/>

</bean>

<!--둘. 스케줄러의 interval time 설정-->

<!--cronExpression을 통해서 스캐줄러 주기를 설정한다. -->

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">

<property name="jobDetail" ref="cronQuartzJob"/>

<property name="cronExpression" value="0/1 * * * * ?"/>

</bean>

<!--셋. 실제 동작하게끔 설정 -->

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<list>

<ref bean="cronTrigger"/>

</list>

</property>

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

     <prop key="org.quartz.threadPool.threadCount">5</prop>

     <prop key="org.quartz.threadPool.threadPriority">4</prop>

     <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

     <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>

</beans>


Cron type 사용하려면 CronExpression 알아야 합니다.

*Cron Expression
cron expression
 각각의 필드는 다음을 나타낸다.(왼쪽 -> 오른쪽 )

 필드 이름

 허용 

 허용된 특수 문자

 Seconds

 0 ~ 59

 , - * /

 Minutes

 0 ~ 59

 , - * /

 Hours

 0 ~ 23

 , - * /

 Day-of-month

 1 ~ 31

 , - * ? / L W

 Month

 1 ~12 or JAN ~ DEC

  , - * /

 Day-Of-Week

 1 ~ 7 or SUN-SAT

 , - * ? / L #

 Year (optional)

 empty, 1970 ~ 2099

 , - * /


Cron Expression 
 특수문자
'*' : 
모든 수를 나타냄분의 위치에 * 설정하면 "  마다" 라는 .
'?' : day-of-month 
 day-of-week 필드에서만 사용가능특별한 값이 없음을 나타낸다.
'-' : "10-12" 
 같이 기간을 설정한다시간 필드에 "10-12" 이라 입력하면 "10, 11, 12시에 동작하도록 설정" 이란 .
',' : "MON,WED,FRI"
 같이 특정 시간을 설정할  사용한다. "MON,WED,FRI" 이면 " ',,에만 동작" 이란 .
'/' : 
증가를 표현합니다예를 들어  단위에 "0/15" 세팅 되어 있다면 "0 부터 시작하여 15 이후에 동작" 이란 .
'L' : day-of-month 
 day-of-week 필드에만 사용하며 마지막날을 나타냅만약 day-of-month  "L"  되어 있다면 이번달의 마지막에 실행하겠다는 것을 나타냄.
'W' : day-of-month 
필드에만 사용되며주어진 기간에 가장 가까운 평일(~) 나타낸다만약 "15W" 이고 이번 달의15일이 토요일이라면 가장가까운 14 금요일날 실행된다 15일이 일요일이라면 가장 가까운 평일인 16 월요일에실행되게 된다만약 15일이 화요일이라면 화요일인 15일에 수행된다.
"LW" : L
 W 결합하여 사용할  있으며 "LW" "이번달 마지막 평일" 나타냄
"#" : day-of-week
 사용된다. "6#3" 이면 3(3)번째  금요일(6) 이란 뜻이된다.1 일요일 ~ 7 토요일 

 Expression

 Meaning

 "0 0 12 * * ?"

 매일 12시에 실행

 "0 15 10 ? * *"

 매일 10 15분에 실행

 "0 15 10 * * ?"

 매일 10 15분에 실행

 "0 15 10 * * ? *"

 매일 10 15분에 실행

 "0 15 10 * * ?  2010" 

 2010 동안 매일 10 15분에 실행

 "0 * 14 * * ?"

 매일 14시에서 시작해서 14:59  끝남

 "0 0/5 14 * * ?"

 매일 14시에 시작하여 5 간격으로 실행되며 14:55분에끝남

 "0 0/5 14,18 * * ?"

 매일 14시에 시작하여 5 간격으로 실행되며 14:55분에끝나고매일 18시에 시작하여 5분간격으로 실행되며18:55분에 끝난다.

 "0 0-5 14 * * ?"

 매일 14시에 시작하여 14:05 분에 끝난다.


시간에 맞춰 돌아가는 스케줄러에서 다른 클래스를 사용하고 싶을 때는 다음과 같이 설정합니다.

<!-- 스프링 DI : 사용할 Service 객체를 생성 -->

<bean id="quartzJobService" class="net.test.quartz.service.impl.QuartzJobServiceImpl"/>


<bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.JobDetailBean">

<property name="jobClass" value="net.test.quartz.SimpleQuartzJob"/>

<!-- 사용하고자 하는 class의 bean id를 등록 -->

<property name="jobDataAsMap">

<map>

<entry key="quartzJobService">

<ref local="quartzJobService"/>

</entry>

</map>

</property>

</bean>



 *두가지 스케줄러를 동시에 실행 시킬때

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

<property name="triggers">

<!--트리거를 두개 생성후 아래와 같이 세팅 -->

<list>

<ref bean="simpleTrigger"/>

<ref bean="cronTrigger"/>

</list>

</property>

<property name="quartzProperties">

<props>

<prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>

     <prop key="org.quartz.threadPool.threadCount">5</prop>

     <prop key="org.quartz.threadPool.threadPriority">4</prop>

     <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>

     <prop key="org.quartz.jobStore.misfireThreshold">60000</prop>

</props>

</property>

</bean>



7) 실제 작동할 Class파일 생성

package net.test.quartz;


import net.test.quartz.service.QuartzJobService;


import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;


public class SimpleQuartzJob extends QuartzJobBean{

//실행될 클래스는 꼭 QuartzJobBean을 상속받아야 되며 

//executeInternal method를 override 하면 자동으로 이 메소드가 실행


//Spring의 DI를 사용하여 Service객체를 setting

//DI를 사용하지 않는다면 필요 없는 부분

private QuartzJobService quartzJobService;

public void setQuartzJobService(QuartzJobService quartzJobService) {

this.quartzJobService = quartzJobService;

}



@Override

protected void executeInternal(JobExecutionContext ex)throws JobExecutionException {

quartzJobService.printLog();

}

}


위와 같은 방식으로 Spring Quartz 사용하여 스케줄러를 사용할  있습니다.

함께 업로드 하는 파일은 제가 직접 작업한 프로젝트이구요함께 확인  보시면 쉽게 쿼츠를 사용하실수 있으실 겁니다.~^^

 SpringQuartz.war

 

출처 - http://javastore.tistory.com/96


===================================================================================


매번 cronTab 으로 배치작업을 실행시키며, 이게 아직 쓰는 놈인지, 언제 만들었는지,
수정하기 번거롭네 등 에로사항이 많았으.. 기억도 안나고.. 그때그때 만든 것들은 버전관리도 안되고,,
해서. 스프링 프로젝트이니, 스프링에서 동작하는 배치를 쓰는게 낫겠다는 생각이 들어 이참에, 배치를 몽땅 quartz 
로 만들어서 정리를 해야겠다는 생각이 들었심.

스프링 프로젝트 환경이 세팅이 되어있다고 치고,

 

*필자의 경우는 
Dynamic Web Project 로 qbatch 라는 프로젝트를 만들었고,


디렉토리 구조는
qbatch
  |_src
  |_WebContent
          |_index.jsp
          |_META-INF
          |_WEB-INF
              |_classes
              |_lib
              |_views
              |_action-servlet.xml
              |_web.xml
              
이렇게 되어있고, 
src 폴더에는 비지니스를 구현한 java 파일들(cotroller/service/dao 등),
classes 에는 src 의 컴파일된 class 파일들 과 applicationContext.xml 및 각종 properties 파일들,
lib 폴더에는 spring.jar, activation.jar, log4j.jar, mysql-connector-java-xxx.jar 등이,
views 에는 각종 jsp 파일들이 위치해있다.

 

암튼 시작해보면,

 

1. 일단 quartz 라이브러리를 다운받는다.
http://terracotta.org/downloads/open-source/destination?name=quartz-1.8.5.tar.gz&bucket=tcdistributions&file=quartz-1.8.5.tar.gz
압축을 풀고 quartz-all-1.8.5.jar 파일을 찾아서 
WEB-INF/lib 폴더 밑에 복사해 넣으면 준비끝.

 

2. 실제 비지니스를 실행할 java 파일을 만든다.            
나의 비지니스 로직은 System.out.println("음. 배치가 실행되었구만~!!!!"); 이다.
이 로직만 빼고, 나머지는 코드는 동일하게 쓰면되심.

src/com/batch/service/SystemOutTestService.java 를 아래와같이 만든다.

 

 

package com.batch.service;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

public class SystemOutTestService extends QuartzJobBean {
 
 
 @Override
 protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException {
  try {
   
   System.out.println("음. 배치가 실행되었구만~!!!!");
  } catch (Exception e) {
   e.printStackTrace();
  }
  
 }

}

 


3. 이제 저 SystemOutTestService.java 가 실행스케줄링 되도록 applicationContext.xml 을 만져주자.
1. 실행할 비지니스로직 클래스를 빈으로 등록.
2. 해당빈이 스케줄을 만들 트리거 설정
3. 실제 동작되도록 설정.

이렇게 3가지만 세팅하면 되심. 아래와같이.

<beans 어쩌구저쩌구.... >

<!-- 1. 실행할 비지니스로직 클래스를 빈으로 등록. -->
<bean id="SystemOutTest" class="org.springframework.scheduling.quartz.JobDetailBean">
 <property name="jobClass" value="com.batch.service.SystemOutTestService"/>
 </bean>

<!-- 2. 해당빈이 스케줄을 만들 트리거 설정 -->
 <bean id="SystemOutTestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="SystemOutTest"/>
  <property name="repeatInterval" value="3000"/> <!--  every 1000(1초)  -->
  <property name="startDelay" value="2000" /><!--  at first execution 2000(2초) 후에 실행  -->
 </bean>
 
<!-- 3. 실제 동작되도록 설정. -->
 <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean" autowire="no">
  <property name="triggers">
   <list>
    <ref bean="SystemOutTestTrigger"/>
   </list>
  </property>
  <property name="quartzProperties">
   <props>
    <prop key="org.quartz.threadPool.class">org.quartz.simpl.SimpleThreadPool</prop>
    <prop key="org.quartz.threadPool.threadCount">5</prop>
    <prop key="org.quartz.threadPool.threadPriority">4</prop>
    <prop key="org.quartz.jobStore.class">org.quartz.simpl.RAMJobStore</prop>
    <prop key="org.quartz.jobStore.misfireThreshold">600000</prop>
   </props>
  </property>
 </bean>
 
 
이제 톰캣 재시작 하면 3초마다 "음. 배치가 실행되었구만~!!!!" 이라고 찍힐거임.
비지니스 로직이 넘 초라하니, 클래스 추가하여 실제 필요한 동작을 하게끔 비지니스 로직에
시간을 들이심이 좋을듯 해서 초간단으로 적었심. 더 세심한 설정은 구글형이나 네이년에
물어보시면 찾으실 수 있으실거임.


출처 - http://jejoong76.blog.me/70125014485


===================================================================================


Spring에서 Quartz JOB 설정 예제

  • 한 시간에 한번 keyGeneratorJob을 실행 시키는 예제 입니다.

applicationContext.xml 설정

<bean id="keyPool" class="com.oracleclub.web.support.KeyPool" init-method="init" lazy-init="true">
  <property name="keySize" value="1000"/>		
</bean>

<!-- jobClass 설정 -->
<bean id="keyGeneratorJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass" value="com.oracleclub.web.job.KeyGeneratorJob"/>
  <property name="jobDataAsMap">
	<map>
      <entry key="keyPool">
   	    <ref bean="keyPool"/>
  	  </entry>
 	</map>
  </property>
</bean>

<!-- jobTrigger 설정 -->
<bean id="keyGeneratorTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail" ref="keyGeneratorJob" />
  <!--  1000 == 1 second, 60minuts=3600000, 1day=86400000-->
  <property name="startDelay" value="3600000" />
  <property name="repeatInterval" value="3600000" />
</bean>

<bean id="schedulerFactory" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >		
  <property name="triggers">
    <list>
      <ref bean="keyGeneratorTrigger"/>
    </list>
  </property>
</bean>

JAVA 예제 소스

  • 위에서 JobClass로 지정한 com.oracleclub.web.job.KeyGeneratorJob 소스 입니다.
  • 아래 소스는 한 시간에 한번씩 암호화 키를 다시 생성하는 예제 입니다.
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * Key Generator Job
 * 
 * @author : oramaster
 * 
 */
public class KeyGeneratorJob extends QuartzJobBean {
  private KeyPool keyPool;
  private final static Log LOG = LogFactory.getLog(KeyGeneratorJob.class);


  @Override
  protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

    List<KeyGenerator> newList = new ArrayList<KeyGenerator>();
    KeyGenerator keyGenerator;

    try {
      //배치때 실행되는 비지니스 로직
      for (int i = 0; i < keyPool.getKeySize(); i++) {
        keyGenerator = new KeyGenerator();
        byte[] originKey = XORMask.getGeneratorKey();

        keyGenerator.setBlowFishKey(BlowFish.encrypt(originKey, BlowFish.getKey()));
        keyGenerator.setOriginKey(originKey);

        newList.add(keyGenerator);
      }

      keyPool.setKeyList(newList);

    } catch (Exception e) {
      LOG.error(e.getMessage());
      throw new JobExecutionException(e.getMessage());
    }
  }

  public void setKeyPool(KeyPool keyPool) {
    this.keyPool = keyPool;
  }
}

문서정보


===================================================================================


Chapter 19. Quartz 혹은 Timer 를 사용한 스케쥴링

19.1. 소개

Spring은 스케쥴링을 지원하는 통합 클래스들을 제공한다. 현재적으로, Spring은 1.3 이후버전 JDK의 일부분인 Timer와 Quartz 스케쥴러 (http://www.quartzscheduler.org)를 지원하고 있다. 이 두개의 스케쥴러들은 각각 Timer 혹은 Triggers에 대한 선택적 참조를 가지는 FactoryBean을 사용하여 세팅된다. 게다가 당신이 타겟 object의 메써드를 편리하게 호출할 수 있도록 도와주는 Quartz 스케쥴러와 Timer에 대한 편의 클래스를 제공한다.(이것은 일반적인 MethodInvokingFactoryBeans와 비슷하다.)

19.2. OpenSymphony Quartz 스케쥴러 사용하기

Quartz는 TriggersJobs 그리고 모든 종류의 jobs를 인식하고 있는 JobDetail를 사용한다. Quartz에 깔려 있는 기본적인 개념을 알고 싶다면, http://www.opensymphony.com/quartz를 찾아보길 바란다. 편리한 사용을 위해서, Spring은 Spring 기반 어플리케이션 내에서 Quartz의 사용을 손쉽게 만들어주는 두 개의 클래스들을 제공한다.

19.2.1. JobDetailBean 사용하기

JobDetail 객체는 job을 실행하기 위해 필요한 모든 정보를 가지고 있다. Spring은 소위 JobDetailBean이라고 불리는 클래스를 제공하는데, 이것은 JobDetail을 합리적인 디폴트값을 가진 실질적인 JavaBean 객체로 만들어준다. 다음의 예제를 보도록 하자.

<bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean">
  <property name="jobClass">
    <value>example.ExampleJob</value>
  </property>
  <property name="jobDataAsMap">
    <map>
      <entry key="timeout"><value>5</value></entry>
    </map>
  </property>
</bean>
			

위의 job detail bean은 job(ExampleJob)을 실행하기 위한 모든 정보를 가지고 있다. 타임아웃은 job data map으로 기술되었다. job data map은 (실행시 넘겨지는) JobExecutionContext를 통해 이용할 수 있지만, JobDetailBean 역시 job data map으로부터 실질적인 job의 프라퍼티들을 매핑할 수 있다. 때문에 이러한 경우, 만약 ExampleJob이 timeout이라는 프라퍼티를 가지고 있다면, JobDetailBean은 그것을 자동으로 적용할 것이다.

package example;

public class ExampleJob extends QuartzJobBean {

  private int timeout;
  
  /**
   * Setter called after the ExampleJob is instantiated
   * with the value from the JobDetailBean (5)
   */ 
  public void setTimeout(int timeout) {
    this.timeout = timeout;
  }
  
  protected void executeInternal(JobExecutionContext ctx)
  throws JobExecutionException {
      // do the actual work
  }
}
			

당신은 job detail bean의 모든 부가적인 세팅들 역시 마찬가지로 이용할 수 있다.

주의: name과 group 프라퍼티를 사용함으로써, 당신은 job의 name과 group을 변경할 수 있다. default로 job의 이름은 job detail bean의 이름과 동일하다.(위의 예에서는 exampleJob이 된다.)

19.2.2. MethodInvokingJobDetailFactoryBean 사용하기

종종 당신은 특정한 객체의 메써드를 호출할 필요가 있을 것이다. 당신은 MethodInvokingJobDetailFactoryBean을 사용하여 다음과 같이 할 수 있다.

<bean id="methodInvokingJobDetail" 
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예는 (아래에 있는) exampleBusinessObject의 doIt을 호출하는 것을 의미한다.

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

<bean id="exampleBusinessObject" class="examples.ExampleBusinessObject"/>
			

MethodInvokingJobDetailFactoryBean을 사용할 때, 메써드를 호출할 한줄짜리 jobs를 생성할 필요가 없으며, 당신은 단지 실질적인 비지니스 객체를 생성해서 그것을 묶기만 하면된다.

default로는 Quartz Jobs는 비상태이며, 상호 작용하는 jobs의 가능성을 가진다. 만약 당신이 동일한 JobDetail에 대해 두 개의 triggers를 명시한다면, 첫번째 job이 끝나기 이전에 두번째가 시작할지도 모른다. 만약 JobDetail 객체가 상태 인터페이스를 구현한다면, 이런 일은 발생하지 않을 것이다. 두번째 job은 첫번째가 끝나기 전에는 시작하지 않을 것이다. MethodInvokingJobDetailFactoryBean를 사용한 jobs가 동시작용하지 않도록 만들기 위해서는, concurrent 플래그를 false로 세팅해주어야 한다.

<bean id="methodInvokingJobDetail"
  class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
    <property name="concurrent"><value>false</value></property>
</bean>
			

주의: 기본적으로 jobs는 concurrent 옵션에 따라 실행될 것이다.

19.2.3. triggers 와 SchedulerFactoryBean을 사용하여 jobs를 묶기

우리는 job details과 jobs를 생성했고, 당신이 특정 객체의 메써드를 호출할 수 있도록 하는 편의클래스 bean을 살펴보았다. 물론, 우리는 여전히 jobs를 그자체로 스케쥴할 필요가 있다. 이것은 triggers와 SchedulerFactoryBean을 사용하여 이루어진다. 여러가지 triggers는 Quartz 내에서 이용할 수 있다. Spring은 편의를 위해 2개의 상속받은 triggers를 기본적으로 제공한다.:CronTriggerBean과 SimpleTriggerBean이 그것이다.

Triggers는 스케쥴될 필요가 있다. Spring은 triggers를 세팅하기 위한 프라퍼티들을 드러내는 SchedulerFactoryBean을 제공하고 있다. SchedulerFactoryBean은 그 triggers와 함께 실질적인 jobs를 스케쥴한다.

다음 두가지 예를 보자.

<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
  <property name="jobDetail">
    <!-- see the example of method invoking job above -->    
    <ref bean="methodInvokingJobDetail"/>
  </property>
  <property name="startDelay">
    <!-- 10 seconds -->
    <value>10000</value>
  </property>
  <property name="repeatInterval">
    <!-- repeat every 50 seconds -->
    <value>50000</value>
  </property>
</bean>

<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
  <property name="jobDetail">
    <ref bean="exampleJob"/>
  </property>
  <property name="cronExpression">
    <!-- run every morning at 6 AM -->
    <value>0 0 6 * * ?</value>
  </property>
</bean>
			

OK, 이제 우리는 두 개의 triggers를 세팅했다. 하나는 10초 늦게 실행해서 매 50초마다 실행될 것이고, 다른 하나는 매일 아침 6시에 실행될 것이다. 모든 것을 완료하기 위해서, 우리는 SchedulerFactoryBean을 세팅해야 한다.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
  <property name="triggers">
    <list>
      <ref local="cronTrigger"/>
      <ref local="simpleTrigger"/>
    </list>
  </property>
</bean>
			

당신이 세팅할 수 있는 더욱 많은 속성들이 SchedulerFactoryBean에 있다. 이를테면, job details에 의해 사용되는 calendars라던가, Quartz를 커스터마이징할 수 있게 하는 프라퍼티같은 것들이 말이다. 더 많은 정보를 위해서는 JavaDoc(http://www.springframework.org/docs/api/org/springframework/scheduling/quartz/SchedulerFactoryBean.html)을 참조하도록 해라.

19.3. JDK Timer support 사용하기

Spring에서 스케쥴링 업무를 처리하는 또다른 방법은 JDK Timer 객체들을 사용하는 것이다. Timers 자체에 대한 더 많은 정보는http://java.sun.com/docs/books/tutorial/essential/threads/timer.html에서 찾아볼 수 있다. 위에서 살펴 본 기본개념들은 Timer support에도 마찬가지로 적용된다. 당신은 임의의 timers를 생성하고 메써드들을 호출하기 위해 timer를 사용한다. TimerFactoryBean을 사용하여 timers를 묶는다.

19.3.1. 임의의 timers 생성하기

당신은 TimerTask를 사용하여 임의의 timer tasks를 생성할 수 있다. 이것은 Quartz jobs와 유사하다

public class CheckEmailAddresses extends TimerTask {

  private List emailAddresses;
  
  public void setEmailAddresses(List emailAddresses) {
    this.emailAddresses = emailAddresses;
  }
  
  public void run() {
    // iterate over all email addresses and archive them
  }
}
			

이것을 묶는 것 역시 간단하다:

<bean id="checkEmail" class="examples.CheckEmailAddress">
  <property name="emailAddresses">
    <list>
      <value>test@springframework.org</value>
      <value>foo@bar.com</value>
      <value>john@doe.net</value>
    </list>
  </property>
</bean>

<bean id="scheduledTask" class="org.springframework.scheduling.timer.ScheduledTimerTask">
  <!-- wait 10 seconds before starting repeated execution -->
  <property name="delay">
    <value>10000</value>
  </property>
  <!-- run every 50 seconds -->
  <property name="period">
    <value>50000</value>
  </property>
  <property name="timerTask">
    <ref local="checkEmail"/>
  </property>
</bean>
			

task를 단지 한번만 실행하고자 한다면, period 속성을 -1(혹은 다른 음수값으)로 바꿔주면 된다.

19.3.2. MethodInvokingTimerTaskFactoryBean 사용하기

Quartz support와 비슷하게, Timer 역시 당신이 주기적으로 메써드를 호출할 수 있도록 하는 요소들을 기술한다.

<bean id="methodInvokingTask" 
  class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
    <property name="targetObject"><ref bean="exampleBusinessObject"/></property>
    <property name="targetMethod"><value>doIt</value></property>
</bean>

위의 예제는 (아래와 같은) exampleBusinessObject에서 호출되는 doIt에서 끝날 것이다

public class BusinessObject {
  
  // properties and collaborators
  
  public void doIt() {
    // do the actual work
  }
}
			

ScheduledTimerTask가 언급된 위의 예제의 참조값을 methodInvokingTask로 변경하면 이 task가 실행될 것이다.

19.3.3. 감싸기 : TimerFactoryBean을 사용하여 tasks를 세팅하기

TimerFactoryBean은 실질적인 스케쥴링을 세팅한다는 같은 목적을 제공한다는 점에서 Quartz의 SchedulerFactoryBean과 비슷하다. TimerFactoryBean는 실질적인 Timer를 세팅하고 그것이 참조하고 있는 tasks를 스케쥴한다. 당신은 대몬 쓰레드를 사용할 것인지 말것인지를 기술할 수 있다.

<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <!-- see the example above -->
      <ref local="scheduledTask"/>
    </list>
  </property>
</bean>
			

끝!


출처 - http://openframework.or.kr/framework_reference/spring/ver1.2.2/html/scheduling.html

Posted by 1010
56. Eclipse Etc.../Eclipse2014. 1. 22. 14:42
반응형

[Eclipse]Courier New 폰트

 

 

증상 : Eclipse Indigo> Window-Preference-General-Appearance-Colors and Font 에서 Courier New 폰트가 존재하지 않는 현상

 

해결 방법 : C:\Windows\Fonts 에서 Courier New 폰트를 우클릭 - 표시 하여 숨김 해제 한다. 

 

Posted by 1010
반응형

1. 개요


나는 원래 Maven 을 사용해 본 적이 없다.

기존의 시스템을 운영하고 유지보수하는 일이 주 업무였기때문에 새로운 기술을  접할 기회가 없었다.


하지만 최근 새 시스템을 운영하기 위해 인수인계를 받고 있는데, 이쪽은 Maven 으로 프로젝트를 관리하고 있었다.

이번 기회에 Maven 을 접해보고 장단점을 실제로 느껴보자.

  

메이븐은 이클립스의 플러그인들 중 하나다.

메이븐의 주요 기능은 개발자가 정의한 자바 프로젝트에서 필요로 하는 각종 라이브러리들과 이들의 종속성을 자동으로 관리해주는 것이다.

 

출처 : http://likebnb.tistory.com/128 

 

Maven 으로 신규 프로젝트를 생성하는 방법은 http://likebnb.tistory.com/128 에 잘 설명되어 있다.

 

내가 필요한건 기존에 Maven 으로 관리하던 프로젝트를 이식받는 방법이다.

아래의 과정을 따라가 보자

 

1. Import - Maven - Check out Maven Projects from SCM 선택

2. 다음 화면에서 SCM URL 을 선택할 수 없음

3. Find more SCM connector 라는 메세지를 클릭

4. Marketplace 에서 SCM 으로 Maven 용 connector 검색

5. 관련 플러그인 설치

6. Eclipse restart 후 다시 Import - Check out Maven Projects from SCM 하면 Svn URL 을 입력해줄 수 있다.

7. Svn URL 입력 후 Checking Out

 

 

그런데 Checking Out 이 진행되다가 한순간 사라져버린다..

왜 그럴까? 




 

2. 문제(1) - 네트웍 프락시 설정


지금 내 PC 에서 인터넷에 접근하기 위해서는 사내 proxy 를 무조건 타게 되어 있다.

maven 은 관련 lib update 등을 위해서는 인터넷의 maven repository 에 접근해야하고, 마찬가지로 proxy 설정이 필요하다.

maven 설치 후 C:\Documents and Settings\사용자\.m2 에 settings.xml 이 없다면 아래와 같이 만들어 주자

(이전 글에서 이클립스에 네트웍 설정으로 세팅한 proxy ip 와 동일한 값이다)

 

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
   <proxies>
    <proxy>
       <active>true</active>
       <protocol>http</protocol>
       <host>70.10.15.10</host>
       <port>8080</port>
     </proxy>
 </proxies>
</settings>

 





2. 문제(2) - Library Jars 인식 불가


maven project 를 check out 후 maven 을 실행해보니 에러가 난다.

자세히 보니 porm.xml 도 에러파일 표시가 나타나고,  buildpath 에서 확인해보면 maven repository 에서 일부 libs 들이 missing 상태로 되어 있다.

물론 관련 jars 들은 프로젝트의 /libs 에 별도로 저장되어 있고, check out 받았다.

 

이러때는 어떻게 해야 할까?

수동으로 찾아서 C:\Users\sds\.m2\repository 아래 디렉토리를 만들어주고 하나하나 챙겨줘야 하나?

위 방법처럼 관리할 바엔 maven을 사용하지 않는게 낫겠다.


인터넷으로는 해결 방법을 찾을 수 없어 http://socurites.com 주인장께 직접 가름침 받았다.


원인은 Maven에서 네트웍 오류 등으로 LastUpdated 실패 내역이 남아있으면 네트웍 문제가 해결된 뒤에도 정상 작동하지 않기 때문이었다.

에러로그를 확인하고 관련된 lattUpdated 파일을 찾아서 지워주자.


[에러메세지 예시]

ArtifactDescriptorException: Failed to read artifact descriptor for asm:asm:jar:3.3: ArtifactResolutionException: Failure to transfer asm:asm:pom:3.3 from http://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact asm:asm:pom:3.3 from/to central (http://repo.maven.apache.org/maven2): C:\Users\사용자\.m2\repository\asm\asm\3.3\asm-3.3.pom.ahc761bb4996fb34ed7 (지정된 파일을 찾을 수 없습니다) 


[C:\Users\사용자\.m2\repository\asm\asm\3.3\asm-3.3.pom.lastUpdated 파일 내용 ]

#NOTE: This is an internal implementation file, its format can be changed without prior notice.

#Tue Jul 09 18:43:57 KST 2013

http\://www.datanucleus.org/downloads/maven2/.error=

legacy-file\:../../local.repository/trunk/.lastUpdated=1373362730367

https\://repository.jboss.org/nexus/content/repositories/thirdparty-uploads/.lastUpdated=1373363027302

http\://maven.springframework.org/milestone/.lastUpdated=1373363037968

http\://repo.maven.apache.org/maven2/.error=Could not transfer artifact asm\:asm\:pom\:3.3 from/to central (http\://repo.maven.apache.org/maven2)\: C\:\\Users\\사용자\\.m2\\repository\\asm\\asm\\3.3\\asm-3.3.pom.ahc761bb4996fb34ed7 (\uC9C0\uC815\uB41C \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4)

file\:../../local.repository/trunk/.error=Could not transfer artifact asm\:asm\:pom\:3.3 from/to local.repository (file\:../../local.repository/trunk)\: No connector available to access repository local.repository (file\:../../local.repository/trunk) of type legacy using the available factories AsyncRepositoryConnectorFactory, WagonRepositoryConnectorFactory

http\://www.datanucleus.org/downloads/maven2/.lastUpdated=1373362730738

http\://maven.springframework.org/milestone/.error=

da39a3ee5e6b4b0d3255bfef95601890afd80709@70.10.15.10\:8080>default-http\://repo.maven.apache.org/maven2/.lastUpdated=1373362730361

https\://repository.jboss.org/nexus/content/repositories/thirdparty-uploads/.error=



해당 파일을 삭제해도 porm.xml 의 error 가 사라지지 않는다면 일부 .m2\repository\ 아래 문제가 되는 일부 lib 디렉토리를 지워버리고 새로 업데이트 해보자.

porm.xml 에 걸려있던 error 들이 한번에 해결되는 경우도 있다.



이제 Maven 실행에 문제가 없을 것이다.


추가로 Maven 은 기본적으로 /test 디렉토리를 가지고 있기 때문에 

소스코드 내에 /test 디렉토리가 없으면 역시 에러가 발생한다.

프로젝트 프로퍼티에서 /test (missing) 을 확인하고 remove 해주던지, /test 를 만들어주던지 하자


출처 : http://originalchoi.tistory.com/category/%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD

Posted by 1010
반응형

 인터넷이 되지 않는 내부망에서 spring project 생성시 caching에 문제가 생길수 있다.

이때 spring-context-support.jar and spring-context.jar on the classpath. 를 잡아주면 어느정도 문제점은 해결된다.

eclipse 가 제대로 인식하지 못하는 버그가 있음

element 'cache:annotation-driven' of schema namespace 'http://www.springframework.org

you need spring-context-support.jar and spring-context.jar on the classpath.

 

Posted by 1010
61.Linux2014. 1. 13. 17:11
반응형

CentOS에서 GNOME 설치

1. yum -y groupinstall "X Window System" "GNOME Desktop Environment"

2. startx

------------------------------
X Window 부팅설정

vim /etc/inittab

id:3:initdefault:  3 -> 5 변경

 

Posted by 1010
반응형

Example: Standard jQuery drag-and-drop

 

출처 : http://wwwendt.de/tech/dynatree/doc/samples.html

 

jquery_dynatree-1_2_5-all.zip

 

This sample uses the standard jQuery draggable and droppable.

Skin:

This tree allows dragging.

This tree allows dropping.

Active node: -
Active node: Sub-item 2.1.1(_4)

Drag me around

Drop something here


Dynatree DEVELOPMENT, jQuery UI 1.8.24, jQuery 1.8.2

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
 
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
 
<title>Dynatree - Example</title>
<!--
  <script src="../jquery/jquery.cookie.js" type="text/javascript"></script>
  <script src="../jquery/jquery.js" type="text/javascript"></script>
  <script src="../jquery/jquery-ui.custom.js" type="text/javascript"></script>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js" type="text/javascript"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js" type="text/javascript"></script>
-->

 
<script src="../jquery/jquery.cookie.js" type="text/javascript"></script>
 
<script src="../jquery/jquery.js" type="text/javascript"></script>
 
<script src="../jquery/jquery-ui.custom.js" type="text/javascript"></script>

 
<link href="../src/skin/ui.dynatree.css" rel="stylesheet" type="text/css" id="skinSheet">
 
<script src="../src/jquery.dynatree.js" type="text/javascript"></script>

 
<style type="text/css">
   
#draggableSample, #droppableSample {
      height
:100px;
      padding
:0.5em;
      width
:150px;
      border
:1px solid #AAAAAA;
   
}
   
#draggableSample {
      background
-color: silver;
      color
:#222222;
   
}
   
#droppableSample {
      background
-color: maroon;
      color
: white;
   
}
   
#droppableSample.drophover {
      border
: 1px solid green;
   
}
 
</style>

 
<!-- (Irrelevant source removed.) -->

<script type="text/javascript"><!--
$
(function(){
 
// --- Initialize first Dynatree -------------------------------------------
  $
("#tree").dynatree({
    initAjax
: {
      url
: "sample-data3.json"
   
},
    onLazyRead
: function(node){
     
// Mockup a slow reqeuest ...
      node
.appendAjax({
        url
: "sample-data2.json",
        debugLazyDelay
: 750 // don't do thi in production code
     
});
   
},
    onActivate
: function(node) {
      $
("#echoActive").text(node.data.title + "(" + node.data.key + ")");
   
},
    onDeactivate
: function(node) {
      $
("#echoActive").text("-");
   
},
    dnd
: {
      revert
: false, // true: slide helper back to source if drop is rejected
      onDragStart
: function(node) {
       
/** This function MUST be defined to enable dragging for the tree.
         *  Return false to cancel dragging of node.
         */

        logMsg
("tree.onDragStart(%o)", node);
       
if(node.data.isFolder){
         
return false;
       
}
       
return true;
     
},
      onDragStop
: function(node) {
        logMsg
("tree.onDragStop(%o)", node);
     
}
   
}
 
});
 
// --- Initialize second Dynatree ------------------------------------------
  $
("#tree2").dynatree({
    initAjax
: {
      url
: "sample-data3.json"
   
},
    onLazyRead
: function(node){
     
// Mockup a slow reqeuest ...
      node
.appendAjax({
        url
: "sample-data2.json",
        debugLazyDelay
: 750 // don't do thi in production code
     
});
   
},
    onActivate
: function(node) {
      $
("#echoActive2").text(node.data.title + "(" + node.data.key + ")");
   
},
    onDeactivate
: function(node) {
      $
("#echoActive2").text("-");
   
},
    onLazyRead
: function(node){
      node
.appendAjax({
        url
: "sample-data2.json"
     
});
   
},
    dnd
: {
      autoExpandMS
: 1000,
      preventVoidMoves
: true, // Prevent dropping nodes 'before self', etc.
      onDragEnter
: function(node, sourceNode) {
       
/* sourceNode may be null for non-dynatree droppables.
         * Return false to disallow dropping on node. In this case
         * onDragOver and onDragLeave are not called.
         * Return 'over', 'before, or 'after' to force a hitMode.
         * Any other return value will calc the hitMode from the cursor position.
         */

        logMsg
("tree.onDragEnter(%o, %o)", node, sourceNode);
       
// For the sake of this example deny dropping over folders
       
if(node.data.isFolder){
         
return false;
       
}
       
return true;
//                return "over";
     
},
      onDragOver
: function(node, sourceNode, hitMode) {
       
/* Return false to disallow dropping this node.*/
//         if(node.data.isFolder){
//           var dd = $.ui.ddmanager.current;
//           dd.cancel();
//           alert("folder");
//         }
        logMsg
("tree.onDragOver(%o, %o, %o)", node, sourceNode, hitMode);
     
},
      onDrop
: function(node, sourceNode, hitMode, ui, draggable) {
       
/* This function MUST be defined to enable dropping of items on the tree.
         * sourceNode may be null, if it is a non-Dynatree droppable.
         */

        logMsg
("tree.onDrop(%o, %o)", node, sourceNode);
       
var copynode;
       
if(sourceNode) {
          copynode
= sourceNode.toDict(true, function(dict){
            dict
.title = "Copy of " + dict.title;
           
delete dict.key; // Remove key, so a new one will be created
         
});
       
}else{
          copynode
= {title: "This node was dropped here (" + ui.helper + ")."};
       
}
       
if(hitMode == "over"){
         
// Append as child node
          node
.addChild(copynode);
         
// expand the drop target
          node
.expand(true);
       
}else if(hitMode == "before"){
         
// Add before this, i.e. as child of current parent
          node
.parent.addChild(copynode, node);
       
}else if(hitMode == "after"){
         
// Add after this, i.e. as child of current parent
          node
.parent.addChild(copynode, node.getNextSibling());
       
}
     
},
      onDragLeave
: function(node, sourceNode) {
       
/** Always called if onDragEnter was called.
         */

        logMsg
("tree.onDragLeave(%o, %o)", node, sourceNode);
     
}
   
}
 
});
 
// --- Initialize simple draggable sample ----------------------------------
  $
("#draggableSample").draggable({
//      revert: "invalid", // slide back, when dropping over non-target
    revert
: function(dropped){
     
// Return `true` to let the helper slide back.
     
if(typeof dropped === "boolean"){
       
// dropped == true, when dropped over a simple, valid droppable target.
       
// false, when dropped outside a drop target.
       
return !dropped;
     
}
     
// Drop comes from another tree. Default behavior is to assume
     
// a valid drop, since we are over a drop-target.
     
// Therefore we have to make an extra check, if the target node
     
// was rejected by a Dynatree callback.
     
var helper = $.ui.ddmanager && $.ui.ddmanager.current && $.ui.ddmanager.current.helper;
     
var isRejected = helper && helper.hasClass("dynatree-drop-reject");
     
return isRejected;
     
},
    connectToDynatree
: true,
    cursorAt
: { top: -5, left:-5 },
    helper
: "clone"
 
});
 
// --- Initialize simple droppable sample ----------------------------------
  $
("#droppableSample").droppable({
    hoverClass
: "drophover",
    addClasses
: true,
//    tolerance: "pointer",
    over
: function(event, ui) {
      logMsg
("droppable.over, %o, %o", event, ui);
   
},
    drop
: function(event, ui) {
     
var source = ui.helper.data("dtSourceNode") || ui.draggable;
      $
(this).addClass("ui-state-highlight").find("p").html("Dropped " + source);
//      alert("dropped");
   
}
 
});
 
<!-- (Irrelevant source removed.) -->
});
--></script>
</head>

<body class="example">
 
<h1>Example: Standard jQuery drag-and-drop</h1>
 
<p class="description">
    This sample uses the standard jQuery draggable and droppable.
 
</p>
 
<div>
    Skin:
   
<select id="skinCombo" size="1">
     
<option value="skin">Standard ('/skin/')</option>
     
<option value="skin-vista">Vista ('/skin-vista/')</option>
   
</select>
 
</div>

 
<table>
 
<thead>
 
<tr>
   
<th>
   
<p>This tree allows dragging.</p>
   
</th>
   
<th>
   
<p>This tree allows dropping.</p>
   
</th>
 
</tr>
 
</thead>
 
<tbody>
 
<tr valign="top">
   
<td>
     
<div id="tree"> </div>
   
</td>
   
<td>
     
<div id="tree2"></div>
   
</td>
 
</tr>
 
<tr>
   
<td>
     
<div>Active node: <span id="echoActive">-</span></div>
   
</td>
   
<td>
     
<div>Active node: <span id="echoActive2">-</span></div>
   
</td>
 
</tr>
 
<tr>
   
<td>
     
<div id="draggableSample" class="ui-widget-content">
       
<p>Drag me around</p>
     
</div>
   
</td>
   
<td>
     
<div id="droppableSample" class="ui-widget-content">
       
<p>Drop something here</p>
     
</div>
   
</td>
 
</tr>
 
</tbody>
 
</table>

 
<!-- (Irrelevant source removed.) -->
</body>
</html>
Posted by 1010
반응형


 menuCd

 highMenuCd

 menuNm

 orderNo

 

 1001

 0

 고객

 1

 1002

 1001

 고객리스트

 2
 1003  1002  대기리스트  3
 1004  0  게시판  4
 1005  1004  자유게시판  5
 1006  1004  유머게시판  6

 


위와 같은 형식의 데이터를 DB에서 뽑아 냈을때 오른쪽과 같이 화면에 표현하고 싶었다.


우선 Tree는 jQuery기반의 드래그앤랍이 기능이 되는 "jquery.dynatree.js" 를 선택했다.


이제 위 2차원 배열형태의 데이터를 dynatree가 요구하는 데이터형태로 변환을 해야 한다.

요구하는 데이터형태는 트리형식으로 아래와 같은 스타일로 만들어주면 된다.


[

{"isFolder":"true","title":"고객","children":[

{"isFolder":"true","title":"고객리스트" }

...

    ]

....

]


위와 같은 형태로 만들려고 삽질 좀 하다가 javascript 기반으로 잘 만들어져있는 소스를 구글링을통해 발견하여 그것을 java기반으로 수정 하였습니다.

(http://programmingsummaries.tistory.com/250)


package com.ezwel.welfare.core.util;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
 
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
 
/**
 * JSON관련 가공에 필요한 함수들
 * @auther ddakker 2013. 12. 12.
 */
public class JsonUtil {
    private static final Log log = LogFactory.getLog(JsonUtil.class);
    /**
     * 2차원 배열의 부모/자식 관계의 데이터를 트리형식으로 재나열 한다.
     * @param list          2차원 배열
     * @param rootId        최상위 id
     * @param idKey         유니크한 키(id가 될 필드명)
     * @param pIdKey        부모키(pId가 될 필드명)
     * @param titleKey      메뉴명이 표시될 필드명
     * @return
     * @auther ddakker 2013. 12. 12.
     */
    public static List<Map<String, Object>> convertorTreeMap(final List<Map<String, Object>> list, String rootId, final String idKey, final String pIdKey, final String titleKey){
        return convertorTreeMap(list, rootId, idKey, pIdKey, titleKey, null);
    }
    /**
     * 2차원 배열의 부모/자식 관계의 데이터를 트리형식으로 재나열 한다.
     * @param list          2차원 배열
     * @param rootId        최상위 id
     * @param idKey         유니크한 키(id가 될 필드명)
     * @param pIdKey        부모키(pId가 될 필드명)
     * @param titleKey      메뉴명이 표시될 필드명
     * @param orderKey      정렬이 필요한경우 정령 필드명
     * @return
     * @auther ddakker 2013. 12. 12.
     */
    public static List<Map<String, Object>> convertorTreeMap(List inList, String rootId, final String idKey, final String pIdKey, final String titleKey, final String orderKey){
        List<Map<String, Object>> treeList = new ArrayList<Map<String,Object>>();   // 최종 트리
         
        if( inList == null || inList.size() == 0 throw new RuntimeException("List<Map> 데이터가 없습니다.");
        if( inList.get(0) == null )                 throw new RuntimeException("Map 데이터가 없습니다.");
         
        final List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); // 원본데이터(Bean일경우 Map으로 변환)
        Iterator iter;
        for( iter=inList.iterator(); iter.hasNext(); ) {
            try{
                Object obj = iter.next();
                if( obj instanceof Map ) {
                    list.add((Map<String, Object>) obj);
                }else{
                    list.add((Map<String, Object>) BeanUtils.describe(obj));
                }
            }catch (Exception e) {
                throw new RuntimeException("Collection -> List<Map> 으로 변환 중 실패: " + e);
            }
        }
         
         
        int listLength = list.size();
        int loopLength = 0;
        final int[] treeLength = new int[] { 0 };
         
        while ( treeLength[0] != listLength && listLength != loopLength++ ) {
            for ( int i=0; i<list.size(); i++ ) {
                Map<String, Object> item = list.get(i);
                if ( rootId.equals((String)item.get(pIdKey)) ) {
                    Map<String, Object> view = new HashMap<String, Object>(item);
                    view.put("title", item.get(titleKey));
                    view.put("children", new ArrayList<Map<String,Object>>());
                     
                    treeList.add(view);
                    list.remove(i);
                     
                    treeLength[0]++;
                     
                     
                    if( orderKey != null ){
                        Collections.sort(treeList, new Comparator<Map<String, Object>>(){
                            public int compare(Map<String, Object> arg0, Map<String, Object> arg1) {
                                // TODO Auto-generated method stub
                                return ((String)arg0.get(orderKey)).compareTo((String)arg1.get(orderKey));
                            }
                        });
                    }
                    view.put("isFolder", "true");
                     
                    break;
                }else{
                    new InnerClass(){
                        public void getParentNode(List<Map<String, Object>> children, Map<String, Object> item ) {
                            for ( int i=0; i<children.size(); i++ ) {
                                Map<String, Object> child = children.get(i);
                                if ( child.get(idKey).equals(item.get(pIdKey)) ) {
                                    Map<String, Object> view = new HashMap<String, Object>(item);
                                    view.put("title", item.get(titleKey));
                                    view.put("children", new ArrayList<Map<String,Object>>());
                                    ((List<Map<String,Object>>) child.get("children")).add(view);
                                     
                                    treeLength[0]++;
                                     
                                    list.remove(list.indexOf(item));
                                    view.put("isFolder", "true");
                                     
                                    if( orderKey != null ){
                                        Collections.sort(((List<Map<String,Object>>) child.get("children")), new Comparator<Map<String, Object>>(){
                                            public int compare(Map<String, Object> arg0, Map<String, Object> arg1) {
                                                // TODO Auto-generated method stub
                                                return ((String)arg0.get(orderKey)).compareTo((String)arg1.get(orderKey));
                                            }
                                        });
                                    }
                                    break;
                                }else{
                                    if( ((List<Map<String,Object>>) child.get("children")).size() > 0 ){
                                        getParentNode((List<Map<String,Object>>) child.get("children"), item);
                                    }
                                }
                            }
                        }
                    }.getParentNode(treeList, item);
                }
            }
        }
        return treeList;
    }
     
    public interface InnerClass {
        public void getParentNode(List<Map<String, Object>> list, Map<String, Object> item );
    }
     
}

출처 : http://ddakker.tistory.com/296

Posted by 1010
반응형

spring 3.2 No setter found for property 'cronExpression' in class


Quartz Scheduler 2.x 적용 중 다음과 같은 오류 발생


spring 3.2 No setter found for property 'cronExpression' in class


해결


spring reference에 나와있는 CronTriggerBean 대신 CronTriggerFactoryBean class를 사용함


1
2
3
4
5
<bean class="org.springframework.scheduling.quartz.CronTriggerBean" id="cronTrigger">
<property ref="exampleJob" name="jobDetail">
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?">
</property></property></bean>


대신에 아래와 같이 CronTriggerFactoryBean을 사용함.


1
2
3
4
5
<bean class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" id="cronTrigger">
<property ref="exampleJob" name="jobDetail">
<!-- run every morning at 6 AM -->
<property name="cronExpression" value="0 0 6 * * ?">
</property></property></bean>


참고


If You are using Spring 3.x & Quartz 2.1.x…

Then do only two changes IN YOUR configuration file 1st : for Simple Trigger

Use class=”org.springframework.scheduling.quartz.SimpleTriggerFactoryBean”> instead of class=”org.springframework.scheduling.quartz.SimpleTriggerBean”>

2nd : for Cron Trigger useclass=”org.springframework.scheduling.quartz.CronTriggerFactoryBean” instead ofclass=”org.springframework.scheduling.quartz.CronTriggerBean”


Spring 3 + Quartz 2 error

 

출처 :http://shuiky.tistory.com/728

Posted by 1010
56. Eclipse Etc.../Eclipse2014. 1. 10. 12:44
반응형

Eclipse에서 javadoc 생성을 하려는데
아래와 같은 에러가 났습니다.

javadoc unmappable character for encoding MS949


코드의 주석에 한글(UTF-8로 인코딩된)을 써서 그런 것 같아 구글링 해봤더니 역시나 그 문제 였네요.

해결 방법은 아래 창이 나타났을 때
VM options

-encoding UTF-8


을 적어주면 해결 됩니다.
그러면 한글이 포함된 JavaDoc을 생성할 수 있습니다.

 


출처 : http://kkoseul.tistory.com/154

 

Posted by 1010
98..Etc/jQuery2014. 1. 9. 17:54
반응형

http://wwwendt.de/tech/dynatree/doc/dynatree-doc.html


Example: Standard jQuery drag-and-drop

This sample uses the standard jQuery draggable and droppable.

Skin: 

This tree allows dragging.

This tree allows dropping.

Active node: -Active node: -

Drag me around

Drop something here


Dynatree DEVELOPMENT, jQuery UI 1.8.24, jQuery 1.8.2

Dynatree documentation

This document describes dynatree version: $Version:$.
Document revision: $Revision:$.
A current version may be found at the project site http://wwwendt.de/tech/dynatree/index.html.

Dynatree is a dynamic JavaScript tree view control with support for checkboxes, drag'n'drop, and lazy loading.

Main features:

  • Open source (MIT and GPL License)
  • Optimized for large dynamic trees (DOM elements are only created when really needed).
  • Programmable through a rich object oriented interface.
  • Support for lazy loading and Ajax.
  • Checkboxes and hierarchical selection.
  • Supports drag and drop.
  • Support for persistence.
  • Keyboard aware.
  • Initializes from HTML code, JSON, or JavaScript objects.
  • Implemented as a jQuery plugin.
    (Note: this doesn't mean that you have to use jQuery for your whole site.)

Dynatree runs best, when the HTML document is rendered in a strict mode like
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
Avoid the quirks mode.

1 Download

You can download the current dynatree package at http://code.google.com/p/dynatree/downloads. It contains everything needed including the source, some documentation and examples.
jQuery is already included, but you can check the jQuery site for the latest versions of jquery.js and ui.core.js.

2 Examples

This documentation contains script examples and links.
See also the Example Browser for some more advanced live demos.

Using the [View source code] link in the Example Browser is probably the best way to learn about Dynatree.

3 Quick start

Let's start with a simple example:

Try this example...
<html>
<head>
   
<!-- Include the required JavaScript libraries: -->
   
<script src='jquery/jquery.js' type="text/javascript"></script>
   
<script src='jquery/jquery-ui.custom.js' type="text/javascript"></script>
   
<script src='jquery/jquery.cookie.js' type="text/javascript"></script>

   
<link rel='stylesheet' type='text/css' href='skin/ui.dynatree.css'>
   
<script src='jquery.dynatree.js' type="text/javascript"></script>

   
<!-- Add code to initialize the tree when the document is loaded: -->
   
<script type="text/javascript">
    $
(function(){
       
// Attach the dynatree widget to an existing <div id="tree"> element
       
// and pass the tree options as an argument to the dynatree() function:
        $
("#tree").dynatree({
            onActivate
: function(node) {
               
// A DynaTreeNode object is passed to the activation handler
               
// Note: we also get this event, if persistence is on, and the page is reloaded.
                alert
("You activated " + node.data.title);
           
},
            persist
: true,
            children
: [ // Pass an array of nodes.
               
{title: "Item 1"},
               
{title: "Folder 2", isFolder: true,
                    children
: [
                       
{title: "Sub-item 2.1"},
                       
{title: "Sub-item 2.2"}
                   
]
               
},
               
{title: "Item 3"}
           
]
       
});
   
});
   
</script>
</head>
<body>
   
<!-- Add a <div> element where the tree should appear: -->
   
<div id="tree"> </div>
</body>
</html>

As an alternative, it is possible to leave away the children option and add a <ul> inside the <div id="tree"> tag instead.
See Initializing the tree structure from a <ul> element for an example.

I am going into more details in the following sections.

4 Initializing the tree

Dynatree is based on and made for jQuery. If you are not familiar with this, you might also want to check the jQuery documentation.

The tree is initialized in the onload event of the html document. In jQuery this is usually done by passing a function to $(..) :

<head>
   
<script type="text/javascript">
        $
(function(){
           
[…]
       
});
   
</script>
</head>

The dynatree widget is then attached to an empty <div > element with a given ID of 'tree'. This id can have any value, it only has to match the jQuery selector, in our case '#tree'.
Options are passed to the dynatree() function as a dictionary in curly braces:

            $("#tree").dynatree({
               
[…]
           
});

4.1 Tree options

Tree options are passed as plain JavaScript objects in curly braces, e.g.
{ … }.

The following script shows the available options.
All options have a reasonable default, so we may only have to pass the onActivate handler.

$("#tree").dynatree({
    title
: "Dynatree", // Tree's name (only used for debug outpu)
    minExpandLevel
: 1, // 1: root node is not collapsible
    imagePath
: null, // Path to a folder containing icons. Defaults to 'skin/' subdirectory.
    children
: null, // Init tree structure from this object array.
    initId
: null, // Init tree structure from a <ul> element with this ID.
    initAjax
: null, // Ajax options used to initialize the tree strucuture.
    autoFocus
: true, // Set focus to first child, when expanding or lazy-loading.
    keyboard
: true, // Support keyboard navigation.
    persist
: false, // Persist expand-status to a cookie
    autoCollapse
: false, // Automatically collapse all siblings, when a node is expanded.
    clickFolderMode
: 3, // 1:activate, 2:expand, 3:activate and expand
    activeVisible
: true, // Make sure, active nodes are visible (expanded).
    checkbox
: false, // Show checkboxes.
    selectMode
: 2, // 1:single, 2:multi, 3:multi-hier
    fx
: null, // Animations, e.g. null or { height: "toggle", duration: 200 }
    noLink
: false, // Use <span> instead of <a> tags for all nodes
   
// Low level event handlers: onEvent(dtnode, event): return false, to stop default processing
    onClick
: null, // null: generate focus, expand, activate, select events.
    onDblClick
: null, // (No default actions.)
    onKeydown
: null, // null: generate keyboard navigation (focus, expand, activate).
    onKeypress
: null, // (No default actions.)
    onFocus
: null, // null: set focus to node.
    onBlur
: null, // null: remove focus from node.

   
// Pre-event handlers onQueryEvent(flag, dtnode): return false, to stop processing
    onQueryActivate
: null, // Callback(flag, dtnode) before a node is (de)activated.
    onQuerySelect
: null, // Callback(flag, dtnode) before a node is (de)selected.
    onQueryExpand
: null, // Callback(flag, dtnode) before a node is expanded/collpsed.

   
// High level event handlers
    onPostInit
: null, // Callback(isReloading, isError) when tree was (re)loaded.
    onActivate
: null, // Callback(dtnode) when a node is activated.
    onDeactivate
: null, // Callback(dtnode) when a node is deactivated.
    onSelect
: null, // Callback(flag, dtnode) when a node is (de)selected.
    onExpand
: null, // Callback(flag, dtnode) when a node is expanded/collapsed.
    onLazyRead
: null, // Callback(dtnode) when a lazy node is expanded for the first time.
    onCustomRender
: null, // Callback(dtnode) before a node is rendered. Return a HTML string to override.
    onCreate
: null, // Callback(dtnode, nodeSpan) after a node was rendered for the first time.
    onRender
: null, // Callback(dtnode, nodeSpan) after a node was rendered.
    postProcess
: null, // Callback(data, dataType) before an Ajax result is passed to dynatree.

   
// Drag'n'drop support
    dnd
: {
       
// Make tree nodes draggable:
        onDragStart
: null, // Callback(sourceNode), return true, to enable dnd
        onDragStop
: null, // Callback(sourceNode)
       
// Make tree nodes accept draggables
        autoExpandMS
: 1000, // Expand nodes after n milliseconds of hovering.
        preventVoidMoves
: true, // Prevent dropping nodes 'before self', etc.
        revert
: false, // true: slide helper back to source if drop is rejected
        onDragEnter
: null, // Callback(targetNode, sourceNode, ui, draggable)
        onDragOver
: null, // Callback(targetNode, sourceNode, hitMode)
        onDrop
: null, // Callback(targetNode, sourceNode, hitMode, ui, draggable)
        onDragLeave
: null // Callback(targetNode, sourceNode)
   
},
    ajaxDefaults
: { // Used by initAjax option
        cache
: false, // false: Append random '_' argument to the request url to prevent caching.
        timeout
: 0, // >0: Make sure we get an ajax error for invalid URLs
        dataType
: "json" // Expect json format and pass json object to callbacks.
   
},
    strings
: {
        loading
: "Loading…",
        loadError
: "Load error!"
   
},
    generateIds
: false, // Generate id attributes like <span id='dynatree-id-KEY'>
    idPrefix
: "dynatree-id-", // Used to generate node id's like <span id="dynatree-id-<key>">.
    keyPathSeparator
: "/", // Used by node.getKeyPath() and tree.loadKeyPath().
    cookieId
: "dynatree", // Choose a more unique name, to allow multiple trees.
    cookie
: {
        expires
: null // Days or Date; null: session cookie
   
},
   
// Class names used, when rendering the HTML markup.
   
// Note:
   
// These settings only apply on initialisation.
   
// If only single entries are passed for options.classNames, all other
   
// values are still set to default.
    classNames
: {
        container
: "dynatree-container",
        node
: "dynatree-node",
        folder
: "dynatree-folder",

        empty
: "dynatree-empty",
        vline
: "dynatree-vline",
        expander
: "dynatree-expander",
        connector
: "dynatree-connector",
        checkbox
: "dynatree-checkbox",
        nodeIcon
: "dynatree-icon",
        title
: "dynatree-title",
        noConnector
: "dynatree-no-connector",

        nodeError
: "dynatree-statusnode-error",
        nodeWait
: "dynatree-statusnode-wait",
        hidden
: "dynatree-hidden",
        combinedExpanderPrefix
: "dynatree-exp-",
        combinedIconPrefix
: "dynatree-ico-",
        hasChildren
: "dynatree-has-children",
        active
: "dynatree-active",
        selected
: "dynatree-selected",
        expanded
: "dynatree-expanded",
        lazy
: "dynatree-lazy",
        focused
: "dynatree-focused",
        partsel
: "dynatree-partsel",
        lastsib
: "dynatree-lastsib"
   
},
    debugLevel
: 1 // 0:quiet, 1:normal, 2:debug
});

Details:

opts.classNames
Type: dictionary, default: $.ui.dynatree.defaults.classNames.
Override class names, that are used, when rendering the HTML markup.
Typically this will require some customization of the CSS file too. 
Note: class names are applied on initialisation only. 
Example:
$("#tree1").dynatree({
    checkbox
: true,
   
// Override class name for checkbox icon:
    classNames
: {checkbox: "dynatree-radio"},
   
[...]
opts.clickFolderMode
Type: integer, default: 3.
Define, how a mouse click will change a folder status.
  1. Single-clicking a folder title (or pressing the [enter] or [space] key) will activate it.
    In this mode documents and folders behave the same.
  2. Single-clicking a folder title expands the node. The folder cannot be activated.
  3. Single-clicking a folder title will activate and expand it.
opts.persist
Type: boolean, default: false.
True: the tree's expansion, activation, focus and selection state is saved to a session cookie, so reloading the page will restore the status.
Notes: this may not work with lazy nodes.
See cookie option.

4.2 Initializing the tree structure

A tree structure is made of nodes. Every node may in turn contain a list child nodes.
A dynatree always has exactly one root node, and all top level nodes of our tree are created as direct descendants.
The root node is usually hidden, so we only see the nodes that we have added.

Dynatree can read it's structure from different sources:

  1. If the children option is passed, it will be used.
  2. Otherwise, if the initAjax option is passed, it will be used.
  3. Otherwise, if the initId option is passed, it will be used.
  4. Otherwise, if the the container <div> element contains a <ul> element, it will be used.
  5. Otherwise, the tree is left empty.
    But we may choose to do so, if we want to modify the tree programmatically.

Methods 1-3 expect a list of node options, as described in the following sections.

4.2.1 Node options

Node options are defined as plain JavaScript objects in curly braces, e.g.
{ … }.
Most of the time we pass a list of node options like this
children: [ { … }, { … }, … ].

The follwing snippet shows the attributes that can be used to define a tree node.
There are reasonable default values for all options, so we may only have to pass a title.

children: [
   
{
    title
: null, // (required) Displayed name of the node (html is allowed here)
    key
: null, // May be used with activate(), select(), find(), ...
    isFolder
: false, // Use a folder icon. Also the node is expandable but not selectable.
    isLazy
: false, // Call onLazyRead(), when the node is expanded for the first time to allow for delayed creation of children.
    tooltip
: null, // Show this popup text.
    href
: null, // Added to the generated <a> tag.
    icon
: null, // Use a custom image (filename relative to tree.options.imagePath). 'null' for default icon, 'false' for no icon.
    addClass
: null, // Class name added to the node's span tag.
    noLink
: false, // Use <span> instead of <a> tag for this node
    activate
: false, // Initial active status.
    focus
: false, // Initial focused status.
    expand
: false, // Initial expanded status.
   
select: false, // Initial selected status.
    hideCheckbox
: false, // Suppress checkbox display for this node.
    unselectable
: false, // Prevent selection.
   
// The following attributes are only valid if passed to some functions:
    children
: null // Array of child nodes.
   
// NOTE: we can also add custom attributes here.
   
// This may then also be used in the onActivate(), onSelect() or onLazyTree() callbacks.
   
},
   
[…]
]

The node options are also passed to the event handlers and can be accessed like this:

onActivate: function(node) {
    alert
("You activated " + node.data.title);
},

Details:

data.activate
If set to true, the node will be initially activated.
data.addClass
Class name that is added to the node's <span> element.
Example:
{ title: "Pretty node", addClass: "customClass1" }
or
<li data="addClass: 'customClass1'">Pretty node
can be styled using css as
span.customClass1 a { background-color: maroon; color: yellow; }
data.children
Array of node options, that are used to generate child nodes.
This option is only valid when passed to certain functions, like DynTreeNode.addChild().
data.expand
If set to true, the node will be initially expanded.
data.focus
If set to true, the node will be initially focused.
data.hideCheckbox
Suppress display of checkbox icon.
It is still possible to (de)select the node using the API, keyboard or initialization data. (The selection state may be visualized by a CSS style.)
See also unselectable.
data.href
Contains the link URL, if the tree was initialized from a <ul> tag:
<div id="tree"><ul>
   
<li class="expanded folder">Search engines
   
<ul>
       
<li><a href="http://www.google.com" target="_self">Google</a>
       
<li><a href="http://www.bing.com">Bing</a>
data.icon
Optional name of an image file relative to the image directory. 
If null specified, a default image is used depending on the node type (folder or document). This is the default.
If false specified, no image is displayed.
data.isFolder
Marks node as folder (treated as a document otherwise).
See Folders and Documents
data.isLazy
Enables delayed loading of the node contents. When a lazy node is expanded for the first time, the onLazyRead() callback is called.
data.key
Uniquely identifies this node. It is optional, but we need it for some functions like tree.activateKey().
If specified, the node's element id is generated by prepending a prefix like this: dynatree-id-1234.
If not specified, a random key id is generated.
data.select
If set to true, the node will be initially selected.
data.target
See data.href.
data.title
Type: string, default: "".
Displayed name of the node (html markup is allowed here).
data.tooltip
Optional string to display in a popup window when the cursor hovers over the node.
data.unselectable
Prevent (de)selection of this node using API, mouse, and keyboard.
It is still possible, to (de)select this node in the initialization data or indirectly (in multi-hier mode).
See also hideCheckbox.

To override the node attribute defaults, modify the structure before initializing dynatree:

<script type="text/javascript">
    $.ui.dynatree.nodedatadefaults["icon"] = false; // Turn off icons by default

    $(function(){
        $("#tree").dynatree({
            rootVisible: false,
            [...]

4.2.2 Folders and documents

When a node is of type folder, it get's a special folder icon and class name.
We usually use them to hold child nodes.
Also, folders can be expanded by clicking the title text (this behavior can be controlled using the clickFolderMode option).

Non-folders ('documents') may also contain child nodes.
Clicking on a child node activates it, so we have to click the small [+] icon in front to expand such a document node.

4.2.3 Initializing the tree structure from an object array

In the quick example above we have already seen how a tree is initialized by passing a node array with the children option.

$("#tree").dynatree({
    children
: [ ],
   
[…]
});

See also Node options.

4.2.4 Initializing the tree structure from an Ajax response

Instead of passing an array of data objects, we can pass a url in the initAjax option that will be used to contact an Ajax web service.

$("#tree").dynatree({
    initAjax
: {url: "/ajaxTree",
               data
: {key: "root", // Optional arguments to append to the url
                      mode
: "all"
                     
}
               
},
   
[…]
});

The web service is expected to return a valid JSON node list, formatted like this:
[ { ... }, { ... }, ... ].

Because the data request is performed asynchronously, the document will load faster. Dynatree will display a spinning wheel, while waiting for the request to complete.

See Loading child nodes on demand for details.
See Persistence for lazy trees for a sample on how to combine this with persistence.

4.2.5 Initializing the tree structure from a <ul> element

If the container <div> contains a <ul> element, the node titles are read from the <li> tags.
If the title contains html markup, it may be better to wrap it inside a span element.

All other node options are specified in the data attribute of a <li> element. For example

<li data="url: 'http://jquery.com'">jQuery home
<li data="url: 'http://example.com', addClass: 'customClass1'">Example page

Note that the data attribute is not valid in <li> elements in some doctypes (HTML 4.01 transitional and Strict and XHTML 1.0 Strict). Validators will complain about this.
Also, if the id attribute is used to pass a key, it should be alphanumeric and start with a letter to be compliant.
(This doesn't seem to affect the functionality however.)

Nested <ul> elements are used to build a hierarchical tree structure.
After the <ul> element was parsed, it is removed from the DOM tree.

Note that <a> elements are recognized:
<li><a href='URL' target='TARGET'>TITLE</a> will result in
node.data.title = TITLE
node.data.href = URL
node.data.target = TARGET

Try this example...
<head>
   
<!-- Include the required JavaScript libraries: -->
   
<script src='jquery/jquery.js' type="text/javascript"></script>
   
<script src='jquery/jquery-ui.custom.js' type="text/javascript"></script>

   
<link rel='stylesheet' type='text/css' href='skin/ui.dynatree.css' >
   
<script src='jquery.dynatree.js' type="text/javascript"></script>

   
<!-- Add code to initialize the tree when the document is loaded: -->
   
<script type="text/javascript">
    $
(function(){
        $
("#tree").dynatree({
            onActivate
: function(node) {
                alert
("You activated " + node);
           
}
       
});
   
});
   
</script>
</head>
<body>
   
<!-- Add a <div> element where the tree should appear: -->
   
<div id="tree">
       
<ul>
           
<li id="key1" title="Look, a tool tip!">item1 with key and tooltip
           
<li id="key2" class="selected">item2: selected on init
           
<li id="key3" class="folder">Folder with some children
               
<ul>
                   
<li id="key3.1">Sub-item 3.1
                   
<li id="key3.2">Sub-item 3.2
               
</ul>

           
<li id="key4" class="expanded">Document with some children (expanded on init)
               
<ul>
                   
<li id="key4.1">Sub-item 4.1
                   
<li id="key4.2">Sub-item 4.2
               
</ul>

           
<li id="key5" class="lazy folder">Lazy folder
       
</ul>
   
</div>
</body>

4.2.6 Initializing the tree structure programmatically

Finally, it is always possible to program the DynaTree and DynaTreeNode objects directly.

See also Programming dynatree.

Try this example...
$(function(){
   
// Initialize the tree in the onload event
    $
("#tree").dynatree({
        onActivate
: function(node) {
            alert
("You activated " + node);
       
}
   
});
   
// Now get the root node object
   
var rootNode = $("#tree").dynatree("getRoot");
   
// Call the DynaTreeNode.addChild() member function and pass options for the new node
   
var childNode = rootNode.addChild({
        title
: "Child node 1",
        tooltip
: "This child node was added programmatically.",
        isFolder
: true
   
});
   
//
    childNode
.addChild({
        title
: "Document using a custom icon",
        icon
: "customdoc1.gif"
   
});
});

5 Handling events

When a user clicks a node, we want to react in some way. So at least we want to implement an onActivate handler.

All event handlers are passed an instance of DynaTreeNode as argument.
this refers to the Dynatree object.
The node options can be accessed like this:

onActivate: function(node) {
    alert
("You activated " + node.data.title);
},

See also Programming dynatree.

5.1 DynaTree callbacks

The this context is set to the tree object.
Use tree.isUserEvent()tree.isInitializing(), and tree.isReloading() to determine who generated this event.

opts.onActivate(node)
Called when a node was activated.
onActivate: function(node) {
   
if(node.tree.isUserEvent()){
       
[...] // Do something after user activated the node (using mouse or keyboard)
   
}
}
opts.onBlur(node)
Called when a node lost the focus.
opts.onClick(node, event)
Called when a node was clicked.
Use node.getEventTargetType(event) to check which area was clicked.
Return false to prevent default processing (setting focus, activate the node, expand folders, etc.).
onClick: function(node, event) {
   
if(node.getEventTargetType(event) == "title"){
       
[...] // Handle the click event
       
return false;// Prevent default processing
   
}
}
opts.onCreate(node, nodeSpan)
Called after a node's HTML tag was created, i.e. when a node becomes visible for the first time.
This callback may be used to bind events or widgets for nodes that are created lazily or programatically.
onCreate: function(node, nodeSpan) {
    $
(span).click(function(e){
        alert
('clicked ' + node);
   
});
}
(Note that the use of jQuery live events may often be a more efficient solution.)
See also opts.onRender.
opts.onCustomRender(node)
Called before a node's title HTML tag will be created. This happens when a node becomes visible for the first time.
This callback may return a string that will be used instead of the default HTML markup.
onCustomRender: function(node) {
   
return "<span class='dynatree-title'>SPAM</span>"
}
opts.onDblClick(node, event)
Called when a node was double clicked.
Use node.getEventTargetType(event) to check which area was clicked.
Return false to prevent default processing (currently none).
opts.onDeactivate(node)
Called when a node was deactivated.
opts.onExpand(flag, node)
Called when a node was expanded/collapsed.
opts.onFocus(node)
Called when a node receives the focus.
opts.onKeydown(node, event)
Called on keydown events.
Return false to prevent default processing (generate keyboard navigation, focus, expand, activate, etc.).
opts.onKeypress(node, event)
Called on keypress events.
Return false to prevent default processing (currently none).
opts.onLazyRead(node)
Called when a lazy node is expanded for the first time.
opts.onPostInit(isReloading, isError [, XMLHttpRequest, textStatus, errorThrown])
Called when the tree was (re)loaded.
In case of an error, isError will be true and addition info is passed: XMLHttpRequest, textStatus, errorThrown.
opts.onQueryActivate(flag, node)
Called before a node is (de)activated. Return false to prevent this.
opts.onQueryExpand(flag, node)
Called before a node is expanded/collapsed. Return false to prevent this.
opts.onQuerySelect(flag, node)
Called before a node is (de)selected. Return false to prevent this.
opts.onRender(node, nodeSpan)
Called after every time a node's HTML tag was created or changed.
This callback may be used to modify the HTML markup.
onRender: function(node, nodeSpan) {
    $
(nodeSpan).find("a.dynatree-title").css("color", "red");
}
See also opts.onCreate.
opts.onSelect(flag, node)
Called when a node was (de)selected.
opts.dnd.onDragStart(sourceNode)
This function must be defined to enable dragging for the tree. Return false to cancel dragging of node.
opts.dnd.onDragEnter(targetNode, sourceNode, ui, draggable)
Return true to make tree nodes accept dropping of draggables.
opts.dnd.onDragOver(targetNode, sourceNode, hitMode, ui, draggable)
opts.dnd.onDragLeave(targetNode, sourceNode, ui, draggable)
opts.dnd.onDrop(targetNode, sourceNode, hitMode, ui, draggable)
This function must be defined to enable dropping of items on the tree.
opts.dnd.onDragStop(sourceNode)
ajaxOptions.success(node)
(Passed as argument to node.appendAjax(...).)
Called after nodes have been created and the waiting icon was removed. 'this' is the options for this Ajax request
ajaxOptions.error(node, XMLHttpRequest, textStatus, errorThrown)
(Passed as argument to node.appendAjax(...).)

5.2 Handling activate/click

The following example handles an activation event by opening a url in a new window.
This assumes, that we have defined an additional custom attribute named 'url' in the node options, like so:

<ul>
   
<li data="url: 'http://jquery.com'">jQuery home
   
<li data="url: 'http://docs.jquery.com'">jQuery docs

or

children: [
   
{ title: "jQuery home", url: "http://jquery.com" },
   
{ title: "jQuery docs", url: "http://docs.jquery.com" },

Also, the title of the currently active node is displayed in the <span id='echoActive'> tag.

Try this example...
$("#tree").dynatree({
   
[…]
    onActivate
: function(node) {
       
if( node.data.url )
            window
.open(node.data.url);
        $
("#echoActive").text(node.data.title);
   
},
    onDeactivate
: function(node) {
        $
("#echoActive").text("-");
   
},
   
[…]
});

5.3 Handling selection events

The following example writes the title of the currently focused node to the <span id='echoFocused'> element:

Try this example...
    $("#tree").dynatree({
       
[…]
        onSelect
: function(flag, node) {
           
if( ! flag )
                alert
("You deselected node with title " + node.data.title);
           
var selectedNodes = node.tree.getSelectedNodes();
           
var selectedKeys = $.map(selectedNodes, function(node){
               
return node.data.key;
           
});
            alert
("Selected keys: " + selectedKeys.join(", "));
       
},
       
[…]
   
});
   

5.4 Handling focus changes

If we use the cursor keys to walk the tree nodes, the focus changes to the next node, but the active node remains the same unless we use [Space] or [Enter].
Also, when we click on a folder node it is only focused, but not activated.

The following example writes the title of the currently focused node to the <span id='echoFocused'> element:

Try this example...
$("#tree").dynatree({
   
[…]
    onFocus
: function(node) {
        $
("#echoFocused").text(node.data.title);
   
},
    onBlur
: function(node) {
        $
("#echoFocused").text("-");
   
},
   
[…]
});

5.5 Loading child nodes on demand ('lazy loading')

Dynatree supports delayed loading of tree nodes, which means we read the child nodes only when their parent is expanded.

Because the data request is performed asynchronously, the browser will not block and is still responsive. Dynatree will display a spinning wheel, while waiting for the request to complete.

To make this happen, we have to

  • Mark some or all nodes as lazy, by setting the isLazy option to true.
  • Implement a backend web service that delivers a JSON formatted node list.
  • Implement the onLazyRead callback to send an Ajax request, create the child nodes, and set the 'ok' status.
Try this example...
$("#tree").dynatree({
   
[…]
    onLazyRead
: function(node){
        node
.appendAjax({url: "/sendData",
                           data
: {"key": node.data.key, // Optional url arguments
                                 
"mode": "all"
                                 
}
                         
});
   
},
   
[…]
});

Typically we would implement onLazyRead by calling the node.appendAjax() function.
It expects one option object argument, as described in the documentation for the jQuery.ajax() command.

These options are set by default:
cache: false and dataType: "json".

Note that the success and error options are implemented differently from the jQuery standard:
They pass different arguments and are called after the Dynatree default processing took place.
This makes it easy to use the success callback to apply any custom postprocessing, for example activating a node or binding events.

$("#tree").dynatree({
   
[…]
    onLazyRead
: function(node){
        node
.appendAjax({url: "/sendData",
                           data
: {"key": node.data.key, // Optional url arguments
                                 
"mode": "all"
                                 
},
                           
// (Optional) use JSONP to allow cross-site-requests
                           
// (must be supported by the server):
//                         dataType: "jsonp",
                           success
: function(node) {
                               
// Called after nodes have been created and the waiting icon was removed.
                               
// 'this' is the options for this Ajax request
                               
},
                           error
: function(node, XMLHttpRequest, textStatus, errorThrown) {
                               
// Called on error, after error icon was created.
                               
},
                           cache
: false // Append random '_' argument to url to prevent caching.
                         
});
   
},
   
[…]
});

The web service is expected to return a valid JSON node list, formatted like this:
[ { "title": "Node1", "isLazy": true, "key": "BC13B21636CD6D5C", … }, { … }, … ]
See Node options for a list of supported attributes.

When the response was received, appendAjax() appends the child nodes and calls node.setLazyNodeStatus(DTNodeStatus_Ok) to remove the wait icon.

Note that initAjax is simply a special case, where the tree's root node is loaded on startup.
See Initializing the structure from an Ajax response for a sample to initialize the whole tree with an Ajax request.

This sample code (written in Python) shows how a server could create a response:

# Build node list as JSON formatted string:
res
= '['
res
+= '{ "title": "Node 1", "key": "k1", "isLazy": true },'
res
+= '{ "title": "Node 2", "key": "k2", "isLazy": true },'
res
+= '{ "title": "Node 3", "key": "k3", "isLazy": true }' # no trailing "," at the last line
res
+= ']'

# Add support for the JSONP protocol:
# This means, if the request URL contains an argument '?callback=xxx',
# wrap the result as 'xxx(result)'
if "callback" in argDict:
    res
= argDict["callback"] + "(" + res + ")"

# Make sure, content type is JSON:
start_response
("200 OK", [("Content-Type", "application/json")])

# Return result (the square brackets are Python / WSGI specific):
return [ res ]

See dynatree_server.py for a sample implementation of a web server that handles this (~150 lines of Python code).
When this server is running, you can try this live example of a lazy tree.

5.5.1 Loading custom formats

If we need more control, or if the server cannot provide JSON in Dynatree's native format, we could also use jQuery.ajax() to fetch the data, then transform it and callnode.addChild():

$("#tree").dynatree({
   
[…]
    onLazyRead
: function(node){
        $
.ajax({
            url
: […],
            success
: function(data, textStatus){
               
// In this sample we assume that the server returns JSON like
               
// { "status": "...", "result": [ {...}, {...}, ...]}
               
if(data.status == "ok"){
                   
// Convert the response to a native Dynatree JavaScipt object.
                   
var list = data.result;
                    res
= [];
                   
for(var i=0, l=list.length; i<l; i++){
                       
var e = list[i];
                        res
.push({title: "" + i + ": " + e.fcurr + "-" + e.tcurr + ":" + e.ukurs,
                            icon
: false});
                   
}
                   
// PWS status OK
                    node
.setLazyNodeStatus(DTNodeStatus_Ok);
                    node
.addChild(res);
               
}else{
                   
// Server returned an error condition: set node status accordingly
                    node
.setLazyNodeStatus(DTNodeStatus_Error, {
                        tooltip
: data.faultDetails,
                        info
: data.faultString
                   
});
               
}
           
}
       
});
   
[…]
});

6 Drag'n'drop

Drag and drop functionality is enabled by defining the appropriate callbacks:

    $("#tree").dynatree({
       
[...]
        dnd
: {
            onDragStart
: function(node) {
               
/** This function MUST be defined to enable dragging for the tree.
                 *  Return false to cancel dragging of node.
                 */

                logMsg
("tree.onDragStart(%o)", node);
               
return true;
           
},
            onDrop
: function(node, sourceNode, hitMode, ui, draggable) {
               
/** This function MUST be defined to enable dropping of items on
                 * the tree.
                 */

                logMsg
("tree.onDrop(%o, %o, %s)", node, sourceNode, hitMode);
                sourceNode
.move(node, hitMode);
           
}
       
}
   
});

There are a lot more callbacks that can be used to fine tune the behaviour. Check the source code in the samples in the Example Browser to learn more.

7 Persistence

When initializing a tree in persist mode, we first check, if persistence cookies already exist.
If not, we assume first-time initializing, read the status from the tree source, and store it into new cookies.

Otherwise we assume re-loading, ignore the source node attributes and override them using the cookie info.

In either case, the 'active', 'expand' and 'select' status of a node is read from the data or restored from the cookies.
However, no onQueryActivate, onActivate, onExpand, onSelect, etc. events are fired. (The only event that may be fired is onFocus.)
In order to generate these events on reload, we may use the callback function onPostInit() and tree.reactivate().

$("#tree").dynatree({
   
[…]
    onPostInit
: function(isReloading, isError) {
       
// 'this' is the current tree
       
// isReloading is true, if status was read from existing cookies
       
// isError is only used in Ajax mode
       
// Fire an onActivate() event for the currently active node
       
this.reactivate();
   
},
    onActivate
: function(node) {
       
// Use status functions to find out about the calling context
       
var isInitializing = node.tree.isInitializing(); // Tree loading phase
       
var isReloading = node.tree.isReloading(); // Loading phase, and reading status from cookies
       
var isUserEvent = node.tree.isUserEvent(); // Event was triggered by mouse or keyboard

        $
("#echoActive").text(node.data.title);
   
},

7.1 Persistence for lazy trees

The problem with restoring the status of a lazy tree is, that the currently active or selected nodes may not be part of the tree, when it is freshly re-loaded.

The basic idea is to leave it up to the backend web service to deliver not only the top-level nodes, but also all nodes that are required to display the current status.

For example, it may be neccessary to render 3 parent nodes, if the active node is at level # 4.
The backend may also deliver all child nodes of expanded parents.
Or in selectMode 3 (hierarchical) we may want to send all nodes, that are partly selected.

initAjax (and appendAjax) have 3 options, that make it easy to pass persistence information to the web service.

See dynatree_server.py for a sample implementation of a web server that handles this (~150 lines of Python code).
When this server is running, you can try this live example of a lazy tree.

$("#tree").dynatree({
   
[…]
    initAjax
: {url: "/ajaxTree",
               data
: {key: key,
                      mode
: mode,
                      filter
: filter
                     
},
               addActiveKey
: true,  // add &activeKey= parameter to URL
               addFocusedKey
: true, // add &focusedKey= parameter to URL
               addExpandedKeyList
: true // add &expandedKeyList= parameter to URL
               
},
    onPostInit
: function(isReloading, isError) {
       
// In lazy mode, this will be called *after* the initAjax request returned.
       
// 'this' is the current tree
       
// isReloading is set, if status was read from existing cookies
       
// isError is set, if Ajax failed
       
// Fire an onActivate() event for the currently active node
       
this.reactivate();
   
},
    onActivate
: function(node) {
       
// Use status functions to find out about the calling context
       
var isUserEvent = node.tree.isUserEvent(); // Event was triggered by mouse or keyboard

        $
("#echoActive").text(node.data.title);
   
},

8 Programming dynatree

The dynatree widget provides a set of plugin methods, that can be called directly.
For example

$("#tree").dynatree("disable");

However this plugin implementation is based on a class called DynaTree that holds a set of DynaTreeNode objects.
These classes expose methods that can be accessed for enhanced functionality.
For example:

// Get the DynaTree object instance:
var tree = $("#tree").dynatree("getTree");
// Use it's class methods:
tree
.activateKey("key1234");
// Get a DynaTreeNode object instance:
var node = tree.getNodeByKey("key7654");
var rootNode = $("#tree").dynatree("getRoot");
// and use it
node
.toggleExpand();

8.1 Dynatree Plugin methods

Besides the constructor, that is called like this:

$("#tree").dynatree({
   
[…]
});

The following methods are globally available from the ui.dynatree namespace:

$.ui.dynatree.getNode(el)
Return a DynaTreeNode object for a given DOM element.
`el` may be a DOM element or a jQuery object. Example:
$("#tree a").hover(function(){
       
var node = $.ui.dynatree.getNode(this);
        logMsg
("Hover in %s", node);
   
}, function(){
       
[...]
   
});
$.ui.dynatree.getPersistData(cookieId, cookieOpts)
Return cookie persistence info as dictionary.
$.ui.dynatree.version
Release version number.

The following methods are directly available from the plugin:

$("#tree").dynatree("disable")
Disable event handling and add a class 'dynatree-disabled' to the container element.
$("#tree").dynatree("enable")
Enable event handling and remove the 'dynatree-disabled' class from the container element.
$("#tree").dynatree("option", )
Set a dynatree option at runtime. Example:
$("#tree").dynatree("option", "autoCollapse", true);
$
("#tree").dynatree("option", "fx", { height: "toggle", duration: 200 });
$("#tree").dynatree("getTree")
Return the associated DynaTree object.
$("#tree").dynatree("getRoot")
Return the root DynaTreeNode object of the tree.
$("#tree").dynatree("getActiveNode")
Return the DynaTreeNode object that is currently active.
(May be null.)
$("#tree").dynatree("getSelectedNodes")
Return an array of DynaTreeNode objects that are currently selected.
(May be empty: [ ].)

8.2 DynaTree class members

tree.activateKey(key)
Activate and focus node with a given key and fire focus and activate events.
If activeVisible option is set, all parents will be expanded as necessary.
If key is null, the current activation is removed.
Return the active DynaTreeNode.
tree.count()
Return the number of nodes.
tree.disable()
Disable input for the tree and display gray. This is a shortcut for $("#tree").dynatreee("disable").
tree.enable()
Complement to tree.disable().
tree.enableUpdate(enable)
Turn rendering on or off and return the previous mode. Disabling update may speedup processing, when adding lots of nodes.
Don't forget to turn rendering back on, after applying the changes:
var prevMode = tree.enableUpdate(false);
[...]
tree
.enableUpdate(prevMode);
tree.getActiveNode()
Return the currently active DynaTreeNode or null.
tree.getNodeByKey(key)
Return DynaTreeNode with a given key or 'null' if not found.
tree.getPersistData()
Return cookie persistence info as dictionary.
There is also a global function available: $.ui.dynatree.getPersistData(cookieId, cookieOpts).
tree.getRoot()
Return the invisible root DynaTreeNode object. All visible toplevel nodes are children of this system node.
tree.getSelectedNodes(stopOnParents)
Return a list of currently selected DynaTreeNodes (may be an empty array).
If stopOnParents is set to true, children of selected nodes are skipped. This may be convenient in selectMode:3 (multi-hier).
tree.initialize()
Constructor (internal use).
tree.isInitializing()
Return true, if the tree is in the init phase.
Use this function in an event handler, to check if the event was fired during a page reload, when the cookie persistence is applied.
tree.isReloading()
Return true, if the tree is in the init phase and persistence is on, and the current status was read from existing cookies.
Use this function in an event handler, to check if the event was fired during a page reload, when the cookie persistence is applied.
tree.isUserEvent()
Return true, if the tree is processing a user event.
Use this function in an event handler, to check if the event was fired in response to a mouse click or key stroke.
Otherwise, the the event was generated by an API call or during initialization.
tree.loadKeyPath(keyPath, callback)
Make sure that a node with a given ID is loaded, by traversing - and loading - its parents. This method is ment for lazy hierarchies.
A callback is executed for every node as we go.
tree.loadKeyPath("/_3/_23/_26/_27", function(node, status){
   
if(status == "loaded") {
       
// 'node' is a parent that was just traversed.
       
// If we call expand() here, then all nodes will be expanded
       
// as we go
        node
.expand();
   
}else if(status == "ok") {
       
// 'node' is the end node of our path.
       
// If we call activate() or makeVisible() here, then the
       
// whole branch will be exoanded now
        node
.activate();
   
}else if(status == "notfound") {
       
var seg = arguments[2],
            isEndNode
= arguments[3];
   
}
});
tree.logDebug(msg), logInfo(msg), logWarning(msg)
(Internal use).
tree.reactivate(setFocus)
Fire onQueryActivate and onActivate events for the currently active node (if there is one).
This may be useful when processing an onPostInit callback.
tree.redraw()
Render all visible nodes. See node.render() for details.
tree.reload()
Reload the the tree.
For lazy trees this is done, by re-submitting the Ajax request that was defined in the initAjax option.
This will not work, if the tree was loaded from an embedded <UL> element, because these elements are removed after they have been rendered.
tree.renderInvisibleNodes()
Force immediate HTML creation for all nodes, even if inside collapsed branches. This may be useful, if we want to bind events or otherwise must access these HTML elements.
It will however degrade performance, especially on large trees!
See node.render() for details.
tree.selectKey(key, flag)
Select or deselect node with a given key and fire focus and select events.
Return the selected DynaTreeNode.
tree.serializeArray(stopOnParents)
Return selected nodes as array of {name: 'TreeName', value: 'NodeKey'} objects, where name is the 'name' attribute of the tree's <div> element.
This format is compatible with jQuery's serializeArray() function and may be used in $.post() calls.
See also the 'form' sample in the Example Browser.
tree.toDict(includeRoot)
Convert the tree into a JavaScript object.
If 'includeRoot' is false or omitted, the result is an array of node dcts.
See node.toDict() for details.
tree.visit(fn, includeRoot)
Call fn(node) for all nodes.
Stop iteration, if fn() returns false. Stop iteration of the current branch, if fn() returns 'skip'.

8.3 DynaTreeNode class members

Attribute 'data'
Use this attribute to access all node options that were passed to create this node.
For example node.data.title or node.data.tooltip. See also Node options.
node.activate()
Activate this node - according to flag - and fire a onActivate event.
If activeVisible option is set, all parents will be expanded as necessary.
Focus is not set.
node.activateSilently()
Same as activate(), but does not fire events.
node.addChild(nodeData[, beforeNode])
Append a new child node.
nodeData may be a node data object as defined in Node options, or an array thereof. Also objects and arrays of type DynaTreeNode are allowed.
Example:
var node = $("#tree").dynatree("getTree").getNodeByKey("1234");
node
.addChild({title: "New Node", key: "3333"});
Since the nodeData may be a nested data structure, it is possible to create a deep hierarchy with one call.
The optional argument beforeNode specifies a child DynaTreeNode that the new node will be inserted before. (If this parameter is null or omitted, the new node will be appended.)
node.appendAjax(ajaxOptions)
Accepts a standard jQuery Ajax option object.
An asynchronous request is started, so this function returns immediately. While loading, a spinning wheel is displayed. On error, a red icon is shown.
The request handler must return a JSON object, formatted like the data's children object.
Use the setLazyNodeStatus() function to display the result.
See Loading child nodes on demand ('lazy loading') for details.
node.countChildren()
Return the number of descendant nodes, i.e. direct and indirect children.
node.deactivate()
Deactivate this node and fire an onDeactivate event.
node.expand(flag)
Expand or collapse this node - according to flag.
node.focus()
Set focus to this node. Parent nodes are expanded, if this is necessary to make it visible.
node.getChildren()
Return list of child nodes or null.
For lazy nodes that have not yet been loaded, undefined is is returned.
node.getEventTargetType(event)
Return the part of a node, that a click event occurred on.
Possible values: 'prefix' 'expander', 'checkbox', 'icon', 'title'.
null is returned else.
Note: there is no check, if the event was fired on this node.
node.getLevel()
Return the depth (i.e. number of parent nodes).
0 is returned for the root node.
node.getNextSibling()
Return the successor node or null.
node.getParent()
Return the parent node or null.
node.getPrevSibling()
Return the predecessor node or null.
node.hasChildren()
Return true if node has child nodes.
Return false if node is a leaf, i.e. has no child nodes.
Return undefined if this is a lazy node, that was not yet successfully loaded.
A test for 'node is surely empty' would be coded like
if(node.hasChildren() === false) ...
node.isActive()
Return true, if this node is activated. Only one tree node may be active at any time.
node.isChildOf(otherNode)
Return true, if this node is a direct child of otherNode.
node.isDescendantOf(otherNode)
Return true, if this node is a descendent (direct or indirect child) of otherNode.
node.isExpanded()
Return true, if the node is expanded.
node.isFirstSibling()
Return true, if this node is the first of all children of the current parent.
node.isFocused()
Return true, if this node is has the focus.
node.isLastSibling()
Return true, if this node is the last of all children of the current parent.
node.isLazy()
Return true, if the node is lazy (loaded or not).
node.isLoading()
Return true, if the node is lazy and currently loading (i.e. an Ajax request is active).
node.isSelected()
Return true, if the node is selected.
node.isStatusNode()
Return true, if this is an temporary status node. Status nodes are created while loading lazy data, to display a throbber or error condition.
node.isVisible()
Return true, if the node is visible, i.e. all parents are expanded.
node.makeVisible()
Expand all parents as neccessary, to make this node visible.
node.move(targetNode, mode)
Move this node to targetNode. Possible mode:
  • child: append this node as last child of targetNode. This is the default. To be compatble with the D'n'd hitMode, we also accept 'over'.
  • before: add this node as sibling before targetNode.
  • after: add this node as sibling after targetNode.
node.reload(force)
Deprecated. Use reloadChildren() instead.
node.reloadChildren(callback)
Discard and reload all children of a lazy node by triggering the onLazyRead event. if callback is passed, it is called after the Ajax request was executed. Example
node.reloadChildren(function(node, isOk){
   
if(!isOk) alert("Node " + node + " could not be reloaded.");
});
node.remove()
Remove this node and descendents from tree.
node.removeChildren()
Remove all child nodes and descendents.
node.render(useEffects, includeInvisible)
Redraw this node with current attributes. All HTML markup is updated and class names are added according to current status.
If this node is expanded, markup for children is recursively generated as well.
  • useEffects:
    (default: false) Set to false to prevent animated expand effects, which would be applied asynchronously.
  • includeInvisible:
    (default: false) Force HTML creation for all descendants, even if inside a collapsed branch.
    This may be useful, if we want to bind events or otherwise access these HTML elements. It will however degrade performance, especially on large trees.

Most of the time, there is no need to call this explicitly, since it is internally called on state changes.
node.resetLazy()
Remove all children from a lazy node and make sure it is collapsed. The node will be re-loaded when expanded the next time.
node.scheduleAction(mode, ms)
Schedule a delayed action. Possible mode:
  • expand: Expand this node after ms microseconds.
  • activate: Activate this node after ms microseconds.
  • cancel: cancel pending action, if any was scheduled.
node.select(flag)
Select or deselect this node - according to flag - and fire an onSelect event.
node.setLazyNodeStatus(status)
Display a dummy child node, to provide feedback, when loading a lazy node's content. 
Possible status:
  • DTNodeStatus_Loading: show a spinning wheel, with 'loading...' message.
  • DTNodeStatus_Error: show an error icon and message.
  • DTNodeStatus_Ok: Remove the status node.
Messages may be customized using the strings.loading and strings.loadError options.
node.setTitle(title)
Change current node title and redraw.
node.sortChildren(cmp, deep)
Sort child list by title.
cmd: optional compare function. If ommitted sorting is done by node titles.
deep: optional: pass true to sort all descendant nodes.
Example
// Custom compare function (optional) that sorts case insensitive
var cmp = function(a, b) {
    a
= a.data.title.toLowerCase();
    b
= b.data.title.toLowerCase();
   
return a > b ? 1 : a < b ? -1 : 0;
};
node
.sortChildren(cmp, false);
node.toDict(recursive, callback)
Convert the node into a JavaScript object.
recursive: set to true, to include child nodes.
callback: (optional) function to allow modifications.
Example
var cb = node.toDict(true, function(dict){
    dict
.title = "Copy of " + dict.title;
   
delete dict.key; // prevent duplicate keys
});
node.toggleExpand()
Toggle expansion state.
Expanding a lazy node, fires a onLazyRead event.
node.toggleSelect()
Toggle selection state.
node.visit(fn, includeSelf)
Call fn(node) for all child nodes.
Stop iteration, if fn() returns false. Stop iteration of the current branch, if fn() returns the string 'skip'.
node.visitParents(fn, includeSelf)
Call fn(node) for all parent nodes.
Stop iteration, if fn(node) returns false.

8.4 Programming examples

The follwing code snippets should give an idea on how to use the API.

8.4.1 Example: Select a node with key '1234'

$("#tree").dynatree("getTree").selectKey("1234");
// or another way:
$
("#tree").dynatree("getTree").getNodeByKey("1234").select();
// .. or yet another way (if 'generateIds' option was enabled):
$
("#dynatree-id-1234").prop("dtnode").select();

8.4.2 Example: Access the currently active node

var node = $("#tree").dynatree("getActiveNode");
if( node ){
    alert
("Currently active: " + node.data.title);
}

8.4.3 Example: Retrieve a node using for a DOM element or jQuery object

$(".dynatree-partsel").each(function(){
   
var node = $.ui.dynatree.getNode(this);
   
[...]
});

8.4.4 Example: Rename the active node

var node = $("#tree").dynatree("getActiveNode");
node
.data.title = "My new title";
node
.render();

8.4.5 Example: Add a child to the active node

var node = $("#tree").dynatree("getActiveNode");
var childNode = node.addChild({
    title
: "My new node",
    tooltip
: "This folder and all child nodes were added programmatically."
});

Note: instead of passing a single child object, we could also pass an array of such objects.
Also, the children may again contain children attributes, thus defining a sub tree.

8.4.6 Example: Add a hover handler and find the hovered node from any sub element

// Bind hover events to the tree's <a> tags:
$
("#tree a").hover(function(){
       
var node = $.ui.dynatree.getNode(this);
        logMsg
("Hover in %s", node);
   
}, function(){
       
var node = $.ui.dynatree.getNode(this);
        logMsg
("Hover out %s", node);
   
});

8.4.7 Example: Expand all nodes

$("#tree").dynatree("getRoot").visit(function(node){
    node
.expand(true);
});

8.4.8 Example: Save current tree status to the backend

// Get a JavaScript object copy of the tree
var dict = $("#tree").dynatree("getTree").toDict();
// ... then use Ajax to send this to your server...

8.4.9 Example: activate a node depending on URL

This sample shows how to parse the page URL and activate a node accordingly: http://server/_test-194.html?activate=_11

// Add a helper function to parse the URL
function getURLParameter(name) {
   
return unescape(
       
(RegExp(name + '=' + '(.+?)(&|$)').exec(location.search)||[,null])[1]
   
);
}
// Evaluate the URL after the tree was loaded
$
(function(){
    $
("#tree").dynatree({
       
[...]
        onPostInit
: function(isReloading, isError) {
           
var key = getURLParameter("activate");
           
if( key ) {
               
this.activateKey(key);
           
}
       
},

9 Theming and translation

The tree's fonts, colors, and icons are defined using CSS, so changing the appearance is simply a matter of including a custom stylesheet or by replacing icons.gif with another version.

Try this example...
<script src="../jquery/jquery.js" type="text/javascript"></script>
<script src="../jquery/jquery-ui.custom.js" type="text/javascript"></script>
<script src="../src/jquery.dynatree.js" type="text/javascript"></script>
<!-- Include the basic stylesheet: -->
<link href="../src/skin-vista/ui.dynatree.css" rel="stylesheet" type="text/css">
<!-- Override CSS with a custom stylesheet : -->
<link href="skin-custom/custom.css" rel="stylesheet" type="text/css" >

<script type="text/javascript">
    $
(function(){
        $
("#tree").dynatree({
           
[…]
       
});
   
});
</script>

Custom.css would include lines like this:

.dynatree-has-children span.dynatree-icon
{
    background
-position: 0 0;
    background
-image: url("doc_with_children.gif");
}

Changing the appearance and icons of single nodes is done by assigning a custom class:

<ul>
   
<li data="addClass: 'custom1'">Document with custom class

or

children: [
   
{ title: "Document with custom class", addClass: "custom1" },

we would then add CSS definitions for the new node to our stylesheet:

span.custom1 a
{
    background
-color: #ffffbb;
    color
: maroon;
}
span
.custom1 span.dynatree-icon
{
    background
-position: 0 0;
    background
-image: url("customDoc2.gif");
}

9.1 Translation

Strings can be translated in the tree options:

$("#tree").dynatree({
   
[…]
    strings
: {
        loading
: "Daten werden geladen…",
        loadError
: "Fehler beim Laden!"
   
},
});

10 Feedback, version history, credits and known issues

10.1 Credits

I am using the planize plugin by Nicolas Perriault for the table of contents.
I am using prettify.js by Mike Samuel for syntax highlighting in the of source code samples.

10.2 Feedback and support

First of all: this is work in progress.
Any kind of feedback is very welcome :-)

  • discussion forum is in place to ask questions or discuss features.
  • Check Stack Overflow for existing questions.
  • Use the Issue Tracker to get a list of known bugs, or vote for a feature.
    Please make sure you searched the group and issue tracker, before adding a new request.
  • If you like: Make a donation.


Posted by 1010
반응형

Remote Repository와 Local Repository 가 있다.

 

의존관계에 있는 artifact 를 취득할 때

HTTP, FTP, SCP 등의 프로토콜로

Remote Repository 에 접근하여

필요한 artifact를 받아

Local Repository 에 저장하여 caching 한다.

 

Local Repository는 maven 실행 유저의

홈디렉토리에 .m2 디렉토리가 생성된다.

(windows의 경우 Documents and Settings 아래 사용자 폴더가 홈 디렉토리다.)

 

의존관계에 있는 artiface 가 필요할 때

우선 Local Repository 에서 검색하고

없는 경우 Remote Repository 에서 받아내려온다.

 

기본 Remote Repository 를 Central Repository 라고 하며

특별한 설정 없이 이용이 가능하다.

http://repo1.maven.org/maven2

 

실제 central repository에서 필요한 것을 찾기는 어려우니

아래 사이트를 사용하면 검색이용이하다.

http://mvnrepository.com

 

repository 에서 필요로하는 artifact를 찾기 위해서는

group id, artifact id, version 정보가 있어야

필요로하는 유일한 artifact를 찾을 수 있다.

 

repository에 등록된 자료가

꼭 maven으로 만들어지지 않더라도

그것을 artifact라고부른다.

 

Posted by 1010
반응형

http://search.maven.org/#browse%7C47

내부망에서 작업할때

jar 파일이 없거나

maven 에러날때 직접 다운

 

The Central Repository Browser

Index of

central/

Name Last Modified Size
HTTPClient/ 01-Nov-2005 717.8 K
abbot/ 12-Feb-2010 1.4 M
acegisecurity/ 23-Dec-2005 4.7 M
activation/ 22-Dec-2005 369 B
activecluster/ 29-Jul-2011 1.5 M
activeio/ 01-Nov-2005 4.6 M
activemq-jaxb/ 01-Nov-2005 0 B
activemq/ 01-Nov-2005 922.5 M
activesoap/ 01-Nov-2005 22.9 M
activespace/ 29-Jul-2011 117.4 K
adarwin/ 01-Nov-2005 51.6 K
aelfred/ 01-Nov-2005 26.7 K
aislib/ 01-Nov-2005 271.5 K
altrmi/ 01-Nov-2005 609.5 K
am/ 12-Feb-2013 104.9 K
andromda/ 01-Nov-2005 104.4 K
annogen/ 01-Nov-2005 2.2 M
ant-contrib/ 09-Aug-2008 3.0 M
ant-doxygen/ 01-Nov-2005 16.4 K
ant/ 01-Nov-2005 25.4 M
ant4eclipse/ 29-Jul-2011 331.6 K
antlr/ 01-Nov-2005 4.9 M
anttex/ 01-Nov-2005 5.0 K
aopalliance/ 28-Oct-2005 26.5 K
apache-jaxme/ 01-Nov-2005 1.1 M
aptconvert/ 01-Nov-2005 442.2 K
ar/ 25-Aug-2012 28.8 M
args4j/ 28-Aug-2012 8.2 M
ashkay/ 01-Nov-2005 20.0 K
ashkelon/ 01-Nov-2005 290.9 K
asia/ 18-Feb-2013 6.6 M
asm/ 07-Jul-2009 9.2 M
aspectj/ 21-Dec-2005 109.6 M
aspectwerkz/ 01-Nov-2005 247.5 M
at/ 07-Jun-2013 21.8 M
au/ 24-Jun-2011 5.9 M
avalon-activation/ 01-Nov-2005 489.9 K
avalon-apps/ 01-Nov-2005 18.6 K
avalon-composition/ 01-Nov-2005 1.4 M
avalon-cornerstone/ 01-Nov-2005 140.1 K
avalon-extension/ 01-Nov-2005 82.1 K
avalon-framework/ 01-Nov-2005 6.1 M
avalon-http/ 19-Jul-2006 27.0 K
avalon-logging/ 01-Nov-2005 175.8 K
avalon-logkit/ 01-Nov-2005 248.5 K
avalon-meta/ 01-Nov-2005 658.9 K
avalon-phoenix/ 01-Nov-2005 284.7 K
avalon-repository/ 01-Nov-2005 650.2 K
avalon-util/ 01-Nov-2005 241.7 K
avalon/ 20-Oct-2006 868.7 K
axion/ 01-Nov-2005 1.2 M
axis/ 20-Oct-2006 26.9 M
axis2/ 05-May-2006 12.8 M
azote/ 01-Nov-2005 47.4 K
backport-util-concurrent/ 20-Feb-2008 5.1 M
backport175/ 01-Nov-2005 4.6 M
barsuift/ 27-May-2010 8.7 M
batik/ 01-Nov-2005 11.2 M
bcel/ 28-Oct-2005 1.7 M
be/ 19-Aug-2013 29.7 M
beehive/ 01-Nov-2005 5.1 M
berkano/ 01-Nov-2005 27.7 M
berkeleydb/ 01-Nov-2005 6.8 M
binky/ 25-Feb-2010 801.2 K
biz/ 11-Apr-2013 55.3 M
blissed/ 01-Nov-2005 1.0 M
boo/ 06-Nov-2006 219.9 M
bouncycastle/ 03-Sep-2009 45.3 M
boxstuff/ 01-Nov-2005 29.1 K
bsf/ 01-Nov-2005 534.0 K
bsh/ 01-Nov-2005 1.2 M
burlap/ 01-Nov-2005 79.6 K
by/ 01-Nov-2012 122.3 M
c10n/ 05-Mar-2013 315.5 K
c3p0/ 01-Nov-2005 5.9 M
ca/ 14-Aug-2013 2.8 G
cactus/ 12-Apr-2008 41.7 M
cargo/ 01-Nov-2005 2.7 M
cas/ 23-Jun-2006 255.3 K
castor/ 06-Dec-2007 202.0 M
cc/ 02-Jul-2013 171.8 M
cewolf/ 01-Nov-2005 515.9 K
cglib/ 11-May-2011 19.0 M
ch/ 09-Aug-2013 456.5 M
charlotte/ 01-Nov-2005 130.6 K
checkstyle/ 28-Oct-2005 12.1 M
classworlds/ 20-May-2010 5.3 M
clickstream/ 24-Nov-2005 9.7 K
clirr/ 01-Nov-2005 36.0 K
clover/ 01-Nov-2005 17.1 M
cmsdeploy/ 07-Dec-2005 749 B
cn/ 19-Sep-2013 2.1 M
co/ 08-Aug-2013 18.5 M
cobertura/ 07-Feb-2006 526.7 K
coconut/ 01-Nov-2005 1.8 M
cocoon/ 10-Jun-2008 203.4 M
code/ 20-May-2010 1.5 M
code316/ 01-Nov-2005 49.9 K
codehaus/ 17-Jul-2008 0 B
colt/ 01-Nov-2005 1.6 M
com/ 20-Sep-2013 142.2 G
commons-attributes/ 04-Jul-2006 348.0 K
commons-beanutils/ 25-Mar-2010 14.1 M
commons-betwixt/ 01-Nov-2005 3.5 M
commons-chain/ 09-Mar-2013 1.2 M
commons-cli/ 09-Mar-2013 786.4 K
commons-codec/ 09-Mar-2013 10.3 M
commons-collections/ 09-Mar-2013 15.9 M
commons-compress/ 01-Nov-2005 124.7 K
commons-configuration/ 09-Mar-2013 26.6 M
commons-daemon/ 09-Mar-2013 11.2 M
commons-dbcp/ 09-Mar-2013 4.1 M
commons-dbutils/ 09-Mar-2013 5.1 M
commons-digester/ 09-Mar-2013 14.4 M
commons-discovery/ 09-Mar-2013 1.4 M
commons-el/ 28-Oct-2005 495.0 K
commons-email/ 09-Mar-2013 939.2 K
commons-fileupload/ 09-Mar-2013 4.3 M
commons-grafolia/ 01-Nov-2005 0 B
commons-grant/ 01-Nov-2005 71.4 K
commons-graph/ 01-Nov-2005 148.5 K
commons-http/ 01-Nov-2005 5.8 K
commons-httpclient/ 20-May-2010 21.6 M
commons-i18n/ 01-Nov-2005 7.2 M
commons-io/ 09-Mar-2013 17.0 M
commons-jdbc2pool/ 01-Nov-2005 467 B
commons-jelly/ 20-May-2010 42.7 M
commons-jexl/ 01-Nov-2005 10.3 M
commons-jux/ 01-Nov-2005 6.4 K
commons-jxpath/ 09-Mar-2013 4.3 M
commons-lang/ 09-Mar-2013 12.3 M
commons-latka/ 01-Nov-2005 152.2 K
commons-launcher/ 04-Jul-2006 247.9 K
commons-logging/ 14-Mar-2013 6.6 M
commons-math/ 09-Mar-2013 6.5 M
commons-messenger/ 01-Nov-2005 1.1 M
commons-modeler/ 01-Nov-2005 1.0 M
commons-naming/ 01-Nov-2005 128.3 K
commons-net/ 09-Mar-2013 27.0 M
commons-pool/ 09-Mar-2013 6.7 M
commons-primitives/ 01-Nov-2005 1.5 M
commons-resources/ 01-Nov-2005 49.5 K
commons-scxml/ 09-Mar-2013 2.7 M
commons-services/ 28-Oct-2005 461 B
commons-sql/ 01-Nov-2005 311.1 K
commons-test/ 08-Nov-2005 16.1 K
commons-threadpool/ 01-Nov-2005 7.2 K
commons-transaction/ 01-Nov-2005 925.7 K
commons-util/ 01-Nov-2005 26.7 K
commons-validator/ 28-Oct-2005 6.6 M
commons-vfs/ 01-Nov-2005 1.1 M
commons-xo/ 01-Nov-2005 31.8 K
concurrent/ 28-Oct-2005 1.7 M
continuum/ 01-Nov-2005 2.4 K
controlhaus/ 01-Nov-2005 186.7 M
cornerstone-connection/ 01-Nov-2005 54.6 K
cornerstone-datasources/ 01-Nov-2005 31.8 K
cornerstone-scheduler/ 01-Nov-2005 61.4 K
cornerstone-sockets/ 01-Nov-2005 50.8 K
cornerstone-store/ 01-Nov-2005 97.0 K
cornerstone-threads/ 01-Nov-2005 52.1 K
cos/ 07-Dec-2005 1.6 K
crimson/ 01-Nov-2005 201.2 K
cryptix/ 01-Nov-2005 473.9 K
cssparser/ 20-May-2010 112.7 K
cz/ 09-Aug-2013 22.3 M
d-haven-event/ 01-Nov-2005 134.9 K
d-haven-eventbus/ 01-Nov-2005 15.3 K
d-haven-mpool/ 01-Nov-2005 23.4 K
dalma/ 19-Aug-2011 1.4 M
dalms/ 19-Aug-2011 9.9 K
damagecontrol/ 01-Nov-2005 156.2 M
dataforge/ 01-Nov-2005 0 B
datasift/ 01-Nov-2005 181.9 K
dbunit/ 01-Nov-2005 1.2 M
de/ 20-Sep-2013 5.0 G
decorutils/ 01-Nov-2005 53.4 K
dentaku/ 01-Nov-2005 70.1 M
directory-asn1/ 01-Nov-2005 668.3 K
directory-authx/ 01-Nov-2005 0 B
directory-clients/ 01-Nov-2005 67.9 K
directory-naming/ 01-Nov-2005 140.1 K
directory-network/ 01-Nov-2005 2.2 M
directory-protocols/ 01-Nov-2005 152.0 K
directory-shared/ 01-Nov-2005 2.4 M
directory/ 25-Dec-2005 40.8 M
displaytag/ 12-Aug-2008 14.3 M
ditchnet/ 24-Nov-2005 28.3 K
dk/ 17-May-2013 76.1 M
dna/ 01-Nov-2005 15.6 M
dnsjava/ 03-Nov-2010 17.2 M
docbook/ 17-Jun-2006 14.0 M
doccheck/ 01-Nov-2005 27.8 K
dom4j/ 01-Nov-2005 8.7 M
domify/ 04-Oct-2010 37.8 K
doxia/ 28-Oct-2005 2.2 M
drone/ 01-Nov-2005 67.0 M
drools-examples/ 01-Nov-2005 14.3 K
drools/ 01-Feb-2007 468.0 M
dsh-vocabulary/ 01-Nov-2005 42.1 K
dtdparser/ 01-Nov-2005 45.7 K
dumbster/ 01-Nov-2005 46.9 K
dwr/ 24-Nov-2005 1.7 M
dynaop/ 01-Nov-2005 86.0 K
easyconf/ 01-Nov-2005 190.2 K
easymock/ 28-Oct-2005 655.8 K
echo/ 01-Nov-2005 330.5 K
echo3/ 20-May-2010 376 B
echo3extras/ 20-May-2010 388 B
eclipse/ 01-Nov-2005 15.7 M
ecs/ 01-Nov-2005 751.4 K
edenlib/ 01-Nov-2005 144.2 K
edtftp/ 01-Nov-2005 35.7 K
edu/ 29-Aug-2013 6.2 G
ehcache/ 28-Oct-2005 403.2 K
ejb/ 07-Dec-2005 0 B
el-impl/ 19-Aug-2011 163.5 K
emberio/ 01-Nov-2005 184.6 K
emma/ 18-Oct-2010 11.0 M
ervacon/ 01-Nov-2005 106.3 K
es/ 19-Feb-2013 221.0 M
esper/ 29-Jan-2008 465.2 M
eu/ 29-Aug-2013 471.3 M
excalibur-altrmi/ 01-Nov-2005 422.8 K
excalibur-cli/ 01-Nov-2005 24.5 K
excalibur-collections/ 01-Nov-2005 65.3 K
excalibur-component-examples/ 01-Nov-2005 14.7 K
excalibur-component-tests/ 01-Nov-2005 15.8 K
excalibur-component/ 01-Nov-2005 455.1 K
excalibur-concurrent/ 01-Nov-2005 30.4 K
excalibur-configuration/ 01-Nov-2005 1.5 M
excalibur-containerkit/ 01-Nov-2005 87.8 K
excalibur-datasource/ 01-Nov-2005 292.7 K
excalibur-event/ 01-Nov-2005 1.6 M
excalibur-extension/ 01-Nov-2005 27.3 K
excalibur-fortress/ 01-Nov-2005 1.1 M
excalibur-i18n/ 01-Nov-2005 528.3 K
excalibur-instrument-manager-interfaces/ 01-Nov-2005 46.8 K
excalibur-instrument-manager/ 01-Nov-2005 345.2 K
excalibur-instrument/ 01-Nov-2005 441.0 K
excalibur-io/ 01-Nov-2005 40.9 K
excalibur-lifecycle/ 01-Nov-2005 72.7 K
excalibur-logger/ 01-Nov-2005 384.1 K
excalibur-monitor/ 01-Nov-2005 99.2 K
excalibur-mpool/ 01-Nov-2005 17.6 K
excalibur-naming/ 01-Nov-2005 65.5 K
excalibur-pool/ 01-Nov-2005 290.3 K
excalibur-sourceresolve/ 01-Nov-2005 392.5 K
excalibur-store/ 01-Nov-2005 115.0 K
excalibur-thread/ 01-Nov-2005 433.6 K
excalibur-util/ 01-Nov-2005 95.6 K
excalibur-xmlutil/ 01-Nov-2005 252.0 K
excalibur/ 01-Nov-2005 1.5 M
exist/ 01-Nov-2005 979.3 K
exml/ 01-Nov-2005 119.6 K
exo/ 01-Nov-2005 16.0 M
exolabcore/ 01-Nov-2005 594.8 K
exteca/ 01-Nov-2005 436.0 K
fastutil/ 01-Nov-2005 97.4 M
fesi/ 01-Nov-2005 1.2 M
fi/ 29-Aug-2013 65.2 M
findbugs/ 06-Nov-2006 16.3 M
flox/ 01-Nov-2005 54.2 K
flux/ 13-Dec-2010 420.9 K
fm/ 16-May-2012 26.4 M
fop/ 01-Nov-2005 11.4 M
forehead/ 01-Nov-2005 64.6 K
formproc/ 01-Nov-2005 89.0 K
foxtrot/ 06-Nov-2011 553.4 K
fr/ 16-Sep-2013 398.9 M
freebxml/ 01-Nov-2005 619.0 K
freemarker/ 01-Nov-2005 5.9 M
fulcrum/ 01-Nov-2005 1.4 M
gabriel/ 01-Nov-2005 2.4 M
gbean/ 01-Nov-2005 0 B
generama/ 18-Jul-2008 1.9 M
genjar/ 01-Nov-2005 31.8 K
genjava/ 01-Nov-2005 4.0 M
geronimo-spec/ 01-Nov-2005 5.5 M
geronimo/ 09-Jan-2007 208.9 M
gg/ 08-May-2012 109.8 K
glassfish/ 19-Aug-2011 3.3 K
gnu-regexp/ 01-Nov-2005 30.7 K
gnu/ 20-May-2010 117.3 K
gov/ 15-Aug-2012 159.8 M
gr/ 08-Jan-2013 11.8 M
graphlayout/ 01-Nov-2005 107.8 K
grizzly-cachetest/ 13-Feb-2009 4.5 M
grizzly/ 19-Aug-2011 31.1 M
groovy-xmlrpc/ 27-May-2006 0 B
groovy/ 01-May-2007 359.6 M
gs/ 08-Sep-2012 2.1 M
gsbase/ 01-Nov-2005 283.7 K
hessian/ 01-Nov-2005 513.4 K
hibernate/ 26-Dec-2008 43.8 M
hivemind/ 01-Nov-2005 5.8 M
howl/ 01-Nov-2005 262.6 K
hsqldb/ 28-Oct-2005 8.1 M
htmlunit/ 01-Nov-2005 7.5 M
httpcomponents-httpcore/ 03-Jul-2007 1.2 M
httpunit/ 19-May-2010 5.6 M
hu/ 24-Apr-2013 7.4 M
hudson/ 15-Dec-2010 78.2 M
ical4j/ 01-Nov-2005 2.7 M
icu/ 03-Nov-2005 564 B
icu4j/ 04-Nov-2005 571 B
idb/ 01-Nov-2005 231.6 K
ie/ 20-May-2010 98.4 K
iirekm/ 19-May-2010 5.1 K
il/ 20-Mar-2012 99.7 K
in/ 20-Sep-2013 4.9 M
info/ 10-May-2013 588.7 M
informa/ 01-Nov-2005 182.0 K
innig/ 01-Nov-2005 322.5 K
io/ 29-Aug-2013 5.3 G
isorelax/ 01-Nov-2005 385.4 K
it/ 21-Jul-2013 7.2 G
itext/ 01-Nov-2005 8.7 M
ivory/ 01-Nov-2005 3.3 M
izpack/ 01-Nov-2005 6.1 M
jaas/ 01-Nov-2005 892 B
jackcess/ 01-Nov-2005 565.7 K
jackson/ 06-Sep-2008 4.2 M
jacl/ 01-Nov-2005 718.2 K
jaf/ 28-Oct-2005 994 B
jaimbot/ 01-Nov-2005 88.0 K
jakarta-regexp/ 01-Nov-2005 29.0 K
jalopy/ 01-Nov-2005 7.0 M
james/ 01-Nov-2005 1.1 M
janino/ 01-Nov-2005 9.2 M
jardiff/ 14-Feb-2006 773.1 K
jarjar/ 01-Nov-2005 225.3 K
jarsync/ 01-Nov-2005 58.5 K
jasper-jsr199/ 19-Aug-2011 7.1 K
jasperreports/ 01-Nov-2005 92.4 M
java2html/ 01-Nov-2005 601.0 K
java3d/ 01-Nov-2005 4.0 M
javacc/ 01-Nov-2005 860.9 K
javaconfig/ 01-Nov-2005 47.4 K
javadb/ 20-Mar-2013 116.9 M
javadoc/ 01-Nov-2005 1.3 K
javaee/ 19-Aug-2011 697.5 K
javagroups/ 01-Nov-2005 2.8 M
javainetlocator/ 01-Nov-2005 260.1 K
javamail/ 01-Nov-2005 3.4 K
javancss/ 01-Nov-2005 3.5 M
javanettasks/ 19-Aug-2011 122.4 M
javassist / 05-Apr-2008 0 B
javassist/ 17-Apr-2010 14.1 M
javatar/ 01-Nov-2005 25.8 K
javax/ 30-Mar-2012 518.5 M
javazoom/ 09-Nov-2012 572.3 K
javolution/ 16-Aug-2011 9.5 M
jawin/ 24-Nov-2005 198.6 K
jaxb/ 19-Aug-2011 102.6 K
jaxen/ 13-Dec-2013 54.1 M
jaxme/ 06-Nov-2006 16.2 M
jaxr-ra/ 20-May-2010 29.5 K
jblanket/ 01-Nov-2005 127.5 K
jboss/ 23-Apr-2013 49.3 M
jca/ 01-Nov-2005 1.8 K
jcache/ 01-Nov-2005 109.2 K
jcharts/ 16-Mar-2012 953.2 K
jcifs/ 23-Mar-2012 3.1 M
jcom/ 24-Nov-2005 56.0 K
jcommon/ 28-Oct-2005 2.1 M
jcoverage/ 01-Nov-2005 201.4 K
jcs-javagroups/ 01-Nov-2005 8.4 K
jcs/ 01-Nov-2005 722.0 K
jcvsii/ 01-Nov-2005 561.6 K
jdbc/ 28-Oct-2005 23.1 K
jdbm/ 01-Nov-2005 328.2 K
jdepend/ 01-Nov-2005 317.5 K
jdiff/ 01-Nov-2005 254.2 K
jdo/ 01-Nov-2005 136.1 K
jdom/ 28-Oct-2005 1.3 M
jdring/ 01-Nov-2005 7.6 K
jdsl/ 01-Nov-2005 132.7 K
jen/ 19-Aug-2011 898.9 K
jencks/ 01-Nov-2005 7.7 M
jep/ 01-Nov-2005 56.5 K
jepi/ 19-Aug-2011 26.1 K
jersey/ 19-Aug-2011 7.3 M
jetty/ 21-Dec-2012 84.8 M
jexcelapi/ 06-Nov-2006 1.7 M
jface/ 01-Nov-2005 60.9 K
jfree/ 26-Sep-2011 64.1 M
jfreechart/ 01-Nov-2005 6.0 M
jgen/ 01-Nov-2005 389 B
jgoodies/ 01-Nov-2005 1.3 M
jgraph/ 01-Nov-2005 4.7 M
jgrapht/ 01-Nov-2005 168.7 K
jgroups/ 19-Apr-2010 187.7 M
jhunlang/ 01-Nov-2005 885.8 K
jini/ 10-Dec-2005 1.2 M
jintention/ 19-May-2010 46.2 K
jisp/ 01-Nov-2005 91.3 K
jivesoftware/ 01-Nov-2005 2.8 M
jlibdiff/ 01-Nov-2005 47.0 K
jline/ 21-May-2011 4.9 M
jmagick/ 28-Feb-2013 378.9 K
jmaki/ 19-Aug-2011 34.9 M
jmdns/ 01-Nov-2005 228.1 K
jmimemagic/ 01-Nov-2005 200.1 K
jmml/ 01-Nov-2005 32.5 K
jmock/ 28-Oct-2005 6.2 M
jms/ 28-Oct-2005 1.9 K
jmscts/ 01-Nov-2005 1.4 M
jmsn/ 01-Nov-2005 87.3 K
joda-time/ 17-Aug-2013 31.0 M
john-test/ 29-May-2008 8.6 M
jotm/ 01-Nov-2005 504.8 K
jp/ 26-Dec-2012 48.0 M
jparsec/ 15-Oct-2012 3.5 M
jpl/ 09-Nov-2012 556.6 K
jpox-dbcp/ 14-Mar-2007 14.1 K
jpox-enhancer/ 14-Mar-2007 170.6 K
jpox-java5/ 14-Mar-2007 67.3 K
jpox/ 21-Jan-2008 77.3 M
jrexx/ 01-Nov-2005 99.5 K
jrms/ 01-Nov-2005 618.6 K
jrobin/ 01-Nov-2005 174.2 K
jruby/ 22-Aug-2007 99.2 M
jsch/ 28-Oct-2005 1.5 M
jsf-extensions/ 19-Aug-2011 26.1 K
jspapi/ 28-Oct-2005 102.6 K
jsptags/ 01-Nov-2005 48.4 K
jstl/ 19-Aug-2011 651.8 K
jstyle/ 01-Nov-2005 18.6 K
jta/ 28-Oct-2005 1.0 K
jtds/ 20-Jan-2006 1.0 K
jtidy/ 28-Oct-2005 330.8 K
juddi/ 01-Nov-2005 0 B
jug/ 01-Nov-2005 70.5 K
jung/ 01-Nov-2005 11.6 M
junit-addons/ 28-Oct-2005 54.7 K
junit-doclet/ 01-Nov-2005 77.5 K
junit/ 05-Oct-2010 13.6 M
junitperf/ 01-Nov-2005 28.9 K
juno/ 19-Aug-2011 31.5 K
jwebunit/ 01-Nov-2005 91.0 K
jxta/ 01-Nov-2005 1.5 M
jython/ 01-Nov-2005 4.4 M
kawa/ 01-Nov-2005 1.3 M
kg/ 20-Feb-2013 3.6 M
kohsuke/ 19-Aug-2011 18.3 K
kxml/ 01-Nov-2005 11.1 K
kxml2/ 01-Nov-2005 32.5 K
ldapd-common/ 01-Nov-2005 1.3 M
ldapsdk/ 01-Nov-2005 257.8 K
li/ 30-Aug-2013 1.1 M
lingo/ 01-Nov-2005 7.3 M
locc/ 01-Nov-2005 309.8 K
log4j/ 20-Apr-2010 28.5 M
log4unit/ 01-Nov-2005 63.6 K
logkit/ 02-Nov-2005 3.1 M
loom/ 01-Nov-2005 42.3 M
lt/ 15-Jan-2013 640.3 K
lucene/ 01-Nov-2005 1.8 M
ma/ 15-Jan-2012 75.1 M
magicGball/ 01-Nov-2005 12.4 K
maps/ 20-May-2010 200.8 K
marmalade/ 22-Jul-2006 1.4 M
maven-integration-test-helper/ 09-Jun-2011 0 B
maven-javanet-plugin/ 19-Aug-2011 1.1 M
maven-new/ 01-Nov-2005 19.2 K
maven-plugins/ 06-Nov-2006 52.3 M
maven-proxy/ 01-Nov-2005 4.7 M
maven-taglib/ 01-Nov-2005 834.6 K
maven-torque-plugin/ 01-Nov-2005 483 B
maven-validator/ 01-Nov-2005 817.1 K
maven-xdoclet2-plugin/ 01-Nov-2005 0 B
maven/ 06-Nov-2006 84.4 M
maxq/ 01-Nov-2005 35.6 K
mckoi/ 01-Nov-2005 1.7 M
me/ 05-Sep-2013 306.4 M
merlin-developer/ 01-Nov-2005 79.5 K
merlin-tutorial/ 01-Nov-2005 12.7 K
merlin/ 01-Nov-2005 281.6 K
messenger/ 01-Nov-2005 83.8 K
metaclass/ 01-Nov-2005 7.7 M
mevenide/ 01-Nov-2005 39.2 M
microcontainer/ 01-Nov-2005 0 B
middlegen/ 01-Nov-2005 551.3 K
milyn/ 17-May-2007 26.5 M
mm-mysql/ 01-Nov-2005 236.0 K
mm/ 01-Nov-2005 236.1 K
mockcreator/ 01-Nov-2005 104.6 K
mockit/ 20-May-2010 9.8 M
mockmaker/ 01-Nov-2005 20.9 K
mockobjects/ 01-Nov-2005 1.3 M
mockrunner/ 24-Nov-2005 404.4 K
modello/ 01-Nov-2005 2.4 K
mrj/ 01-Nov-2005 18.9 K
mstor/ 01-Nov-2005 232.2 K
msv/ 01-Nov-2005 6.5 M
mule/ 10-Apr-2007 658.7 M
muse/ 01-Nov-2005 1.0 M
mx/ 04-Jun-2013 16.6 M
mx4j/ 01-Nov-2005 7.8 M
myfaces/ 21-Nov-2005 12.0 M
mysql/ 06-Oct-2007 198.8 M
name/ 10-Sep-2013 10.2 M
nanning/ 01-Nov-2005 331.4 K
nanocontainer/ 14-Jun-2006 57.0 M
nekohtml/ 02-Jun-2008 2.4 M
neo/ 01-Nov-2005 20.6 M
net/ 16-Sep-2013 35.1 G
netbeans/ 01-Nov-2005 1.5 M
new/ 20-May-2010 92.1 K
nf/ 04-Jan-2012 227.7 K
nl/ 26-Jul-2013 1.7 G
no/ 01-May-2013 257.1 M
norbert/ 01-Nov-2005 477.4 K
nsuml/ 01-Nov-2005 3.0 M
nu/ 08-Jun-2012 22.7 M
nz/ 22-May-2012 316.3 M
oauth/ 28-Jan-2010 2.1 M
odmg/ 28-Oct-2005 14.2 K
ognl/ 15-Dec-2010 15.1 M
ojb/ 01-Nov-2005 15.5 M
ojdbc/ 28-Oct-2005 725 B
old/ 01-Nov-2005 2.2 M
oness/ 01-Nov-2005 386.8 K
open-esb/ 19-Aug-2011 29.5 K
open/ 20-May-2010 111.3 K
openejb/ 07-May-2006 392.6 M
openim/ 01-Nov-2005 1.3 M
openjms/ 01-Nov-2005 3.9 M
opennms/ 01-Nov-2005 557.1 K
opensymphony/ 19-Feb-2009 86.9 M
oracle/ 19-Aug-2011 12.1 K
org/ 08-Mar-2013 731.5 G
oro/ 28-Oct-2005 617.1 K
oscube/ 01-Nov-2005 1.1 M
p2psockets/ 01-Nov-2005 29.5 K
p6spy/ 13-Nov-2013 2.3 M
patterntesting/ 01-Nov-2005 10.5 K
payload/ 01-Nov-2005 407.3 K
pcj/ 01-Nov-2005 2.6 M
pdfbox/ 01-Nov-2005 7.6 M
penguin/ 01-Nov-2005 32.6 K
petridish/ 01-Nov-2005 801 B
ph/ 21-Jun-2012 3.4 M
piccolo/ 01-Nov-2005 115.1 K
picocontainer/ 14-Jun-2006 35.0 M
picounit/ 01-Nov-2005 2.9 M
pircbot/ 08-Apr-2011 3.4 M
pl/ 16-Sep-2013 234.1 M
plexus/ 05-Jan-2007 12.2 M
plj/ 01-Nov-2005 1.1 M
plugin/ 01-Nov-2005 0 B
pluto-container/ 01-Nov-2005 113.2 K
pmd/ 12-Nov-2011 73.2 M
pnuts/ 11-Oct-2010 608.0 K
poi/ 29-Oct-2005 20.0 M
poolman/ 01-Nov-2005 89.0 K
portlet-api/ 01-Nov-2005 17.9 K
postgresql/ 05-Oct-2010 17.7 M
prevayler/ 01-Nov-2005 4.2 M
pro/ 15-Feb-2013 7.0 M
proctor/ 19-Aug-2011 2.7 M
profiler/ 01-Nov-2005 9.8 K
proxool/ 28-Oct-2005 466.0 K
proxytoys/ 01-Nov-2005 2.3 M
pt/ 08-Jul-2013 456.6 K
pubscribe/ 01-Nov-2005 1.1 M
pull-parser/ 28-Oct-2005 157.3 K
qdox/ 28-Oct-2005 3.4 M
qfork/ 01-Nov-2005 0 B
quartz-jboss/ 25-Feb-2010 1.6 K
quartz-oracle/ 25-Feb-2010 1.1 K
quartz-weblogic/ 25-Feb-2010 1.3 K
quartz/ 01-Nov-2005 4.1 M
quilt/ 01-Nov-2005 259.3 K
radeox/ 01-Nov-2005 677.3 K
readline/ 01-Nov-2005 453 B
redhill/ 01-Nov-2005 288.1 K
redis/ 06-Oct-2010 6.4 M
redmine/ 20-May-2010 821.1 K
regexp/ 20-Oct-2006 109.8 K
relaxngDatatype/ 01-Nov-2005 31.6 K
reportrunner/ 01-Nov-2005 315.1 K
rhino/ 01-Nov-2005 12.7 M
ro/ 29-Aug-2013 255.4 M
robo-guice/ 20-May-2010 536 B
roboguice/ 20-May-2010 167.5 K
roller/ 20-May-2010 89.5 K
rome/ 19-Aug-2011 8.8 M
rs/ 01-Dec-2012 193.6 K
rss4j/ 01-Nov-2005 46.0 K
rsslibj/ 01-Nov-2005 20.3 K
ru/ 05-Sep-2013 142.9 M
sablecc/ 01-Nov-2005 1.7 M
sax/ 01-Nov-2005 28.3 K
saxon/ 20-Oct-2006 1.9 M
saxpath/ 28-Oct-2005 160.7 K
scout/ 29-Oct-2005 76.5 K
scraping-engine/ 01-Nov-2005 3.0 M
se/ 05-Sep-2013 2.5 G
securityfilter/ 01-Nov-2005 96.9 K
servicemix-ws/ 01-Nov-2005 0 B
servicemix/ 25-Mar-2006 342.3 M
servletapi/ 29-Oct-2005 696.3 K
servlets/ 01-Nov-2005 118.7 K
setpoint/ 06-Nov-2006 193.8 K
sfx4j/ 19-Aug-2011 517.5 K
shellix/ 20-May-2010 789 B
shocks/ 01-Nov-2005 9.1 M
sillyexceptions/ 01-Nov-2005 5.6 K
simple-jms/ 09-Dec-2005 373.5 K
simple-jndi/ 01-Nov-2005 3.1 M
sk/ 27-Feb-2013 453.4 M
skaringa/ 01-Nov-2005 89.2 K
skinlf/ 01-Nov-2005 383.4 K
slide/ 01-Nov-2005 5.9 M
smartrics/ 05-Oct-2012 6.4 M
soap/ 01-Nov-2005 848.4 K
soimp/ 19-Aug-2011 15.7 K
solarisrealm/ 20-May-2010 85.1 K
speexx/ 01-Nov-2005 501.6 K
spice/ 01-Nov-2005 8.9 M
spring/ 01-Nov-2005 2.0 M
springframework/ 01-Nov-2005 85.1 M
springmodules/ 20-Jun-2006 346.4 K
sshtools/ 20-May-2010 1.5 M
sslext/ 01-Nov-2005 94.3 K
stapler/ 19-Aug-2011 77.5 K
statcvs/ 01-Nov-2005 2.5 M
stax-utils/ 01-Nov-2005 112.4 K
stax/ 06-Nov-2006 1.6 M
stratum/ 01-Nov-2005 5.6 M
struts-menu/ 01-Nov-2005 869.9 K
struts/ 01-Nov-2005 11.8 M
strutstestcase/ 01-Nov-2005 1.1 M
stxx/ 01-Nov-2005 245.7 K
subpersistence/ 01-Nov-2005 237.3 K
subshell/ 01-Nov-2005 57.2 K
suiterunner/ 01-Nov-2005 501.9 K
surefire/ 28-Oct-2005 695.4 K
swarmcache/ 28-Oct-2005 31.6 K
swt/ 01-Nov-2005 9.7 M
sysunit/ 01-Nov-2005 1.0 M
tablelayout/ 01-Nov-2005 107.4 K
tagalog/ 01-Nov-2005 31.5 K
tagishauth/ 01-Nov-2005 53.1 K
taglibrarydoc/ 01-Nov-2005 608.4 K
taglibs/ 01-Nov-2005 30.1 M
tagsoup/ 05-Nov-2005 599 B
tambora/ 01-Nov-2005 4.7 M
tanukisoft/ 07-Mar-2008 3.3 M
tapestry/ 01-Nov-2005 36.5 M
tclib/ 01-Nov-2005 67.4 K
textarea/ 01-Nov-2005 66.9 K
thaiopensource/ 20-Oct-2006 779.6 K
tiffrenderer/ 01-Nov-2005 199.8 K
tjdo/ 01-Nov-2005 1.6 M
tk/ 07-Mar-2012 677.0 K
tl/ 11-Jul-2013 4.2 M
tmporb/ 01-Nov-2005 4.2 M
to/ 06-Nov-2012 9.9 M
tomcat-util/ 01-Nov-2005 843 B
tomcat/ 24-Jan-2008 64.4 M
tonic/ 08-Nov-2005 141.6 K
toplink/ 19-Aug-2011 598.8 M
torque-gen/ 01-Nov-2005 429 B
torque/ 12-Jan-2008 10.2 M
touchstone/ 01-Nov-2005 0 B
traer/ 03-Apr-2012 74.4 K
trail-taglib/ 01-Nov-2005 196.9 K
tranql/ 01-Nov-2005 6.9 M
trove/ 01-Nov-2005 999.4 K
turbine/ 01-Nov-2005 38.3 M
tv/ 21-Jul-2011 113.5 M
tw/ 12-Jun-2013 1.3 M
tyrex/ 01-Nov-2005 433.0 K
ua/ 06-Aug-2013 10.8 M
uaihebert/ 22-Feb-2013 832.3 K
ubique/ 01-Nov-2005 9.6 K
uispec4j/ 01-Nov-2005 2.5 M
uk/ 10-Jan-2013 825.2 M
urbanophile/ 01-Nov-2005 53.9 K
urlrewrite/ 24-Nov-2005 37.3 K
us/ 29-Aug-2013 72.3 M
vdoclet/ 01-Nov-2005 197.4 K
velocity-anakia/ 30-Apr-2007 22.1 K
velocity-dvsl/ 01-Nov-2005 4.2 M
velocity-tools/ 21-Dec-2012 1.6 M
velocity/ 30-Apr-2007 6.2 M
village/ 01-Nov-2005 142.9 K
vu/ 20-Jul-2013 1.5 M
wadi/ 01-Nov-2005 2.3 M
webmacro/ 01-Nov-2005 693.8 K
webtest/ 01-Nov-2005 395.7 K
werken-xpath/ 28-Oct-2005 75.4 K
werkflow/ 01-Nov-2005 792.5 K
werkz/ 01-Nov-2005 387.7 K
westhawk/ 01-Nov-2005 317.6 K
which/ 01-Nov-2005 18.1 K
wicket/ 12-Aug-2008 156.1 M
woodstox/ 14-Mar-2008 40.1 M
wrapper/ 01-Nov-2005 30.2 K
ws-commons-java5/ 01-Nov-2005 24.3 K
ws-commons-util/ 01-Nov-2005 104.3 K
ws-commons/ 19-Sep-2006 2.2 M
ws-scout/ 18-Feb-2006 146.9 K
ws/ 12-Jan-2013 14.8 M
wsdl4j/ 28-Feb-2013 3.9 M
wsrf/ 01-Nov-2005 4.4 M
wss4j/ 03-May-2006 1.0 M
wstx/ 01-Nov-2005 1016.4 K
wurfl/ 01-Nov-2005 145.8 K
wutka/ 01-Nov-2005 56.9 K
xajile/ 03-Aug-2009 0 B
xalan/ 30-Sep-2008 32.1 M
xbean/ 05-Jan-2006 537.6 K
xdoclet-plugins/ 03-Jul-2006 7.9 M
xdoclet/ 18-Jul-2008 7.0 M
xerces/ 16-Aug-2011 55.4 M
xercesjarv/ 01-Nov-2005 71.8 K
xfire-root/ 01-Nov-2005 4.9 K
xfire/ 06-Nov-2006 172.9 M
xjavadoc/ 01-Nov-2005 463.1 K
xml-apis/ 20-Aug-2011 5.1 M
xml-resolver/ 01-Nov-2005 345.3 K
xml-security/ 20-Oct-2006 2.1 M
xmlbeans/ 04-Jul-2007 28.3 M
xmldb/ 01-Nov-2005 96.2 K
xmlenc/ 01-Nov-2005 40.2 K
xmlmind/ 01-Nov-2005 210.3 K
xmlpull/ 01-Nov-2005 48.7 K
xmlrpc-helma/ 01-Nov-2005 54.4 K
xmlrpc/ 25-Jun-2006 2.2 M
xmlunit/ 08-Feb-2013 1.3 M
xmlwise/ 27-Oct-2012 81.4 K
xmlwriter/ 01-Nov-2005 763.3 K
xom/ 20-Oct-2006 3.8 M
xpp3/ 28-Oct-2005 3.0 M
xsddoc/ 01-Nov-2005 609.9 K
xsdlib/ 01-Nov-2005 489.9 K
xstream/ 28-Nov-2006 35.7 M
xtc/ 03-Apr-2012 5.6 M
xtiff-jai/ 01-Nov-2005 50.0 K
xxl/ 01-Nov-2005 1.7 M
yan/ 14-Jan-2006 4.6 M
ymsg/ 01-Nov-2005 371.8 K
yom/ 01-Nov-2005 110.8 K
za/ 23-May-2013 170.6 K
THE_NEW_REPO1_MACHINE.txt 27-May-2007 0 B
archetype-catalog.xml 14-Sep-2013 1.6 M
archetype-catalog.xml.md5 02-Aug-2013 32 B
archetype-catalog.xml.sha1 02-Aug-2013 40 B
diff.sh 28-Oct-2005 251 B
index.html.bak 03-Feb-2010 90 B
last_updated.txt 22-Sep-2013 29 B
maven-metadata.xml.md5 06-Dec-2007 0 B
maven-metadata.xml.sha1 06-Dec-2007 0 B
robots.txt 10-Nov-2009 26 B
rss.xml 06-May-2012 152.0 K
Posted by 1010
반응형

Hi I am developing a web application with eclipse and I generate the project with a maven archetype.

When I enable maven dependecy management, eclipse mark some errors in the pom file, this error is:

 Multiple annotations found at this line:
- Execution default-testResources of goal org.apache.maven.plugins:maven-resources-          plugin:2.4.3:testResources failed: 
 Plugin org.apache.maven.plugins:maven-resources-plugin:2.4.3 or one of its dependencies could not be resolved: Failed to collect 
 dependencies for org.apache.maven.plugins:maven-resources-plugin:jar:2.4.3 () (org.apache.maven.plugins:maven-resources-
 plugin:2.4.3:testResources:default-testResources:process-test-resources)
- Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile 
 (execution: default-compile, phase: compile)
- CoreException: Could not get the value for parameter compilerId for plugin execution default-compile: 
 PluginResolutionException: Plugin org.apache.maven.plugins:maven-compiler-plugin:2.3.2 or one of its dependencies could not be 
 resolved: Failed to collect dependencies for org.apache.maven.plugins:maven-compiler-plugin:jar:2.3.2 (): 
 ArtifactDescriptorException: Failed to read artifact descriptor for org.apache.maven:maven-plugin-api:jar:2.0.6: 
 ArtifactResolutionException: Failure to transfer org.apache.maven:maven-plugin-api:pom:2.0.6 from http://repo1.maven.org/
 maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or 
 updates are forced. Original error: Could not transfer artifact org.apache.maven:maven-plugin-api:pom:2.0.6 from/to central (http://
 repo1.maven.org/maven2): null to http://repo1.maven.org/maven2/org/apache/maven/maven-plugin-api/2.0.6/maven-plugin-
 api-2.0.6.pom
- CoreException: Could not get the value for parameter compilerId for plugin execution default-testCompile: 
 PluginResolutionException: Plugin org.apache.maven.plugins:maven-compiler-plugin:2.3.2 or one of its dependencies could not be 
 resolved: Failed to collect dependencies for org.apache.maven.plugins:maven-compiler-plugin:jar:2.3.2 (): 
 ArtifactDescriptorException: Failed to read artifact descriptor for org.apache.maven:maven-plugin-api:jar:2.0.6: 
 ArtifactResolutionException: Failure to transfer org.apache.maven:maven-plugin-api:pom:2.0.6 from http://repo1.maven.org/
 maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or 
 updates are forced. Original error: Could not transfer artifact org.apache.maven:maven-plugin-api:pom:2.0.6 from/to central (http://
 repo1.maven.org/maven2): null to http://repo1.maven.org/maven2/org/apache/maven/maven-plugin-api/2.0.6/maven-plugin-
 api-2.0.6.pom
- Execution default-resources of goal org.apache.maven.plugins:maven-resources-plugin:2.4.3:resources failed: Plugin 
 org.apache.maven.plugins:maven-resources-plugin:2.4.3 or one of its dependencies could not be resolved: Failed to collect 
 dependencies for org.apache.maven.plugins:maven-resources-plugin:jar:2.4.3 () (org.apache.maven.plugins:maven-resources-
 plugin:2.4.3:resources:default-resources:process-resources)
- Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin:
 2.3.2:testCompile (execution: default-testCompile, phase: test-compile)

My pom file is the following:

<project xmlns="http://maven.apache.org/POM/4.0.0"                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.lala.sarasa</groupId>
   <artifactId>msrdecision</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>msrdecision Maven Webapp</name>
  <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>hello</finalName>
   </build>
 </project>

Any idea? Regards

share|improve this question
 
The errors hint that maven can't reach a remote repository. Does "mvn clean package" work on the console? –  KarlsFriend Jan 12 '12 at 12:33
 
no don't work, same error –  McSas Jan 12 '12 at 12:40
1  
my coworkers that use Windows are with same problem. I'm using ubuntu, same version of maven and eclipse, same project... and everything works fine... –  caarlos0 Apr 24 '12 at 18:04
add comment

12 Answers

In my case the problem was solved by Window -> Preferences -> Maven -> User Settings -> Update Settings. I don't know the problem cause at the first place.

share|improve this answer
add comment

I had the same problem but with an other cause. The solution was to deactivate Avira Browser Protection (in german Browser-Schutz). I took the solusion from m2e cannot transfer metadata from nexus, but maven command line can. It can be activated again ones maven has the needed plugin.

share|improve this answer
add comment

I also had same problem with Eclipse 3.7.2 (Indigo) and maven 3.0.4.

Eclipse wasn't picking up my maven settings, so this is what I did to fix the problem:

  1. Window - Preferences - Maven - Installations

    • Add (Maven 3.0.4 instead of using Embedded)
    • Click Apply & OK
  2. Maven > Update Project Configuration... on project (right click)

  3. Shutdown Eclipse

  4. Run mvn install from the command line.

  5. Open Eclipse

Those steps worked for me, but the problem isn't consistent. I've only had with issue on one computer.

share|improve this answer
add comment

I had a similar case for the default groovy compiler plugin

The solution was to install ME2 provided by Springsource according to this answer

Plugin execution not covered by lifecycle configuration maven error

This immediately solved the "Plugin execution not covered by lifecycle configuration" problem in Eclispe Juno.

share|improve this answer
add comment

in my case there was a dependency without a version. in eclipse m2e does not force you to enter a version so i left it blank. once i put the version in pom.xml from within eclipse all was fine

share|improve this answer
add comment

This error bothered me for two days. I tried all kinds of solutions, nothing worked. Finally I give up and tried a radical approach. and it worked! Here are the steps:

  1. Save a copy of the pom.xml,
  2. deleted all text in pom.xml
  3. Let eclipse do a clean rebuild, of course everything will be broken.
  4. pasted back everything in the original pom.xml
  5. let Eclipse build again

I guess it was "indeterministic" as the following article said.

http://wiki.eclipse.org/M2E_plugin_execution_not_covered

share|improve this answer
add comment

I would add some points that helped me to solve this problem :

Having the local repository OK, on the other hand, turned out to be quite costly, as many archetypes would not get loaded, due apparently to timeouts of m2eclipse and very unstable communication speeds in my case.

In many cases only the error file could be found in the folder ex : xxx.jar.lastUpdated, instead of the jar or pom file. I had always to suppress this file to permit a new download.

Also really worthy were :

  • as already said, using the mvn clean install from the command line, apparently much more patient than m2eclipse, and also efficient and verbose, at least for the time being.

  • (and also the Update Project of the Maven menu)

  • downloading using the dependency:get goal

    mvn org.apache.maven.plugins:maven-dependency-plugin:2.1:get -DrepoUrl=url -Dartifact=groupId:artifactId:version1

(from within the project folder) (hint given in another thread, thanks also).

  • also downloading and installing manually (.jar+.sha1), from in particular, "m2proxy atlassian" .

  • adding other repositories in the pom.xml itself (the settings.xml mirror configuration did'nt do the job, I don't know yet why). Ex : nexus/content/repositories/releases/ et nexus/content/repositories/releases/, sous repository.jboss.org, ou download.java.net/maven/2 .

To finish, in any case, a lot of time (!..) could have been et could certainly still be spared with a light tool repairing thoroughly the local repository straightaway. I could not yet find it. Actually it should even be normally a mvn command ("--repair-local-repository").

share|improve this answer
add comment

It must be a configuration problem. Your pom is fine.

Here's what I did. New folder, put your pom inside. Then in eclipse: import -> maven -> existing maven project. Dependencies got included in the project.

I did this using eclipse helios. I think the plugin version I am using is 0.12

You should check the maven properties in eclipse.

share|improve this answer
 
where are the maven properties in eclipse? –  McSas Jan 12 '12 at 12:32
 
I tried to put the pom file in a new folder. When I try to import the project eclipse show two erros: * No marketplace entries found to handle maven-compiler-plugin:2.3.2:testCompile in Eclipse. and * No marketplace entries found to handle maven-compiler-plugin:2.3.2:compile in Eclipse. // any idea? –  McSas Jan 12 '12 at 12:38
add comment

I solved this by running mvn -U install from command line and then "Maven->Update Project" from Eclipse

share|improve this answer
 
That solved my issue to, thanks! –  hithwen Jan 17 '13 at 16:12
1  
I had to first edit the pom.xml file to add the dependency on the module that was causing the problem (org.apache.maven.plugins:maven-jar-plugin, in my case), then OUTSIDE OF ECLIPSE run mvn clean and then mvn -U install in the project folder. Then I restarted eclipse and did a clean/build on the project and All Was Well. –  Laura Apr 23 '13 at 12:44
add comment

In this particular case, the solution was the right proxy configuration of eclipse (Window -> Preferences -> Network Connection), the company possessed a strict security system. I will leave the question, because there are answers that can help the community. Thank you very much for the answers above.

share|improve this answer
add comment

Just go to your user folder, inside it there's a ".m2" folder, open it and delete the folder "repository". Go to eclipse, clean your project, then right click->Maven->Update Project .. and you are ready to go.

share|improve this answer
 
That solve my issue! Very nice. :) –  Jojas Mar 23 '13 at 8:21
 
solved my issue as well! thanks –  Iced_Earth Aug 1 '13 at 20:54
 
Solved my problem..Thanks..:) –  james Sep 28 '13 at 11:46
add comment

I had same problem with Eclipse 3.7.2 (Indigo) and maven 3.0.4.

In my case, the problem was caused by missing maven-resources-plugin-2.4.3.jar in {user.home}\.m2\repository\org\apache\maven\plugins\maven-resources-plugin\2.4.3 folder. (no idea why maven didn't update it)

Solution:

1.) add dependency to pom.xml

<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.4.3</version>
</dependency>

2.) run mvn install from Eclipse or from command line

3.) refresh the project in eclipse (F5)

4.) run Maven > Update Project Configuration... on project (right click)

JAR file is downloaded to local repository and there are no errors in WS.

share|improve this answer
 
didn't work here... –  caarlos0 Apr 24 '12 at 18:04
 
did you checked that there is org\apache\maven\plugins\maven-resources-plugin\2.4.3 in repository ? –  Betlista Apr 24 '12 at 19:32
 
yep, 2.3, 2.4.3 and 2.5. Actually, after a lot of refreshes, mvn install eclipse:eclipse, cleans, close and reopen eclipse, it finally seem to work... but, it become with another error: "Unknow error". any idea? –  caarlos0 Apr 24 '12 at 20:14
 
Try to look into {eclipse.workspace}\.metadata\.log. –  Betlista Apr 25 '12 at 6:30

 

Posted by 1010
반응형

 


Plugin execution not covered by lifecycle configuration: org.apache.maven.plugins:maven-compiler-plugin:2.3.2:testCompile (execution: default-testCompile, phase: test-compile)


CoreException: Could not calculate build plan: Plugin org.apache.maven.plugins:maven-compiler-plugin:2.3.2 or one of its dependencies could not be resolved: Failed to read artifact descriptor for org.apache.maven.plugins:maven-compiler-plugin:jar:2.3.2: ArtifactResolutionException: Failure to transfer org.apache.maven.plugins:maven-compiler-plugin:pom:2.3.2 from http://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced. Original error: Could not transfer artifact org.apache.maven.plugins:maven-compiler-plugin:pom:2.3.2 from/to central (http://repo.maven.apache.org/maven2): null to http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-compiler-plugin/2.3.2/maven-compiler-plugin-2.3.2.pom


으악...도저히 모르겠다..해결방법을..ㅠ.ㅠ...



찾았다..ㅋㅋㅋ해결방법을~!!!

[결과]

 



참고링크!!
http://stackoverflow.com/questions/8834806/m2eclipse-error


해결방법

접기


Solution:

1.) add dependency to pom.xml

<dependency>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>2.4.3</version>
</dependency>

2.) run mvn install from Eclipse or from command line

3.) refresh the project in eclipse (F5)

4.) run Maven > Update Project Configuration... on project (right click)

JAR file is downloaded to local repository and there are no errors in WS.

접기


- 끝 -

 

출처: http://acet.pe.kr/194

Posted by 1010
반응형

No grammar constraints (DTD or XML schema) detected for the document.

신경쓰이게 xml 파일에 warnning 이 나오는데 여간 신경 쓰이는 것이 아니었다.

처음 프로그램 배울 때 작은 warnning 이라도 무시 말라고 했건만...... 쩝

검색과 지식의 양을 늘리는 길만이 살길인것 같다.

 

[Eclipse] Galileo 버전 기준으로

Window - Preoferences - XML - XML Files - Valodation 에서 indicate when no grammar is specified 값을

Warnning 에서 Ignore 로 변경 한다. 

 

그러면 눈에 가시였던 줄과 warnning 이 사라진다.

 

[추가]

[펌] 엄지 사랑님이 작성

 

만약 아래와 같이 했는데도 적용이 되지 않는다면 Project -> Clean 을 통해 해당 프로젝트를 리빌드한다.

 

참고사이트
http://www-01.ibm.com/support/docview.wss?rs=2044&context=SSCM72&dc=DB560&dc=DB520&uid=swg21330232&loc=en_US&cs=UTF-8&lang=en&rss=ct2044rational

 

<script src="http://fordev.tistory.com/plugin/CallBack_bootstrapper?&src=http://cfs.tistory.com/blog/plugins/CallBack/callback&id=35&callbackId=fordevtistorycom353310&destDocId=callbacknestfordevtistorycom353310&host=http://fordev.tistory.com&float=left&random=395"></script>

 

Posted by 1010
반응형

Maven project 를 import 하였을 때 pom.xml 파일에서 다음과 같은 validation error 가 표시될 경우.(실제로 pom.xml 파일이 문제가 없는데도 불구하고)


다음의 플러그인을 설치해 주고 proejct rebuild(clean) 해주면 error mark 가 사라진다.
https://repository.sonatype.org/content/repositories/forge-sites/m2eclipse-tycho/0.6.0/N/0.6.0.201112050222/





Posted by 1010
반응형

출처 : http://bryan7.tistory.com/163

 

"쉽게 따라하는 자바웹개발" project error/warning 없애기.

도서명: 쉽게 따라하는 자바웹개발

출판사: 인사이트

지은이: 백기선

출판일: 2012년 11월 30일


1.3.2 프로젝트 다운로드 (10 page)

http://whiteship.me/book/legacy-sample.zip   (파일 다운로드)

https://github.com/keesun/legacy-sample    ( STS 에서 Import > Git )


개발툴: STS(Spring Tool Suite) Version: 3.4.0.RELEASE      Build Id: 201310051818


나는 STS 의 Import 메뉴를 이용해 github 에서 import  했다.


프로젝트를 빌드하면서 나는 대부분의 에러는 Maven 관련 에러였다.

인터넷을 통해 라이브러리(jar파일)을 가져오면서 완전히 다 못 가져온다던지, 파일 크기는 맞아도 jar 파일의 압축을 풀어보면 헤더가 손상되었다는 메시지가 출력되는 경우 등. - SHA-1 의 checksum 을 계산해서 ~.sha1 파일과 비교해보면 jar 파일이 온전한지를 확인할 수 있다.


이클립스(혹은 STS)에서 Problems View 에서 jar 파일명이 나오는 경우는 어느 jar 파일이 문제인지 쉽게 알 수 있지만, class 파일이 없다고 나오는 경우도 있고, - 이런 경우는 인터넷에서 검색해서 그 class 파일이 어느 jar 에 속해 있는지 알아야 한다.

어떤 경우는 Problems View 에는 error 가 없어도 실행해보면 에러가 나는 경우도 있다. 그러면 로그 파일을 보고 인터넷 검색을 해봐서 어느 클래스에서 문제가 있는지 확인해보고, 그 클래스가 속한 jar 파일이 온전한지를 체크해봐야 한다.


jar 파일의 checksum 이 맞지 않는다면 그 jar 가 속한 폴더를 통째로 지우고, 이클립스에서 clean 하고 다시 빌드를 한다.


나는 우리집 인터넷이 네트워크가 불안하다고는 생각하지 않고 있었는데, - 파일을 다운로드 받으면서 깨지거나 그런 경우는 없었다. - maven 을 통해 라이브러리를 가져오는 것이 왜 이렇게 불안한 것일까? maven 에서 다운로드 받는 로직을 개선해서 다운로드 받은 파일의 유효성을 체크해야 되는 것이 아닐까?


내 컴퓨터 내의 maven repository 내의 모든 jar 파일의 checksum 을 계산해서 맞지 않는 폴더를 출력해주는 스크립트라도 만들어 놔야 할 듯하다.


Spring 이나 Maven 을 처음 접해보는 사람이라면 책의 처음 예제에서 이런 많은 에러가 난다면 아마 4분의 3 은 포기하지 않을까? 좋은 책인데, Maven 관련해서 이렇게 많은 문제가 있는지 나도 실제로 돌려보고야 알았다.

봄싹 Google Groups 에 올라온, 이 책과 관련된 수많은 질문은 대부분 Maven 때문이었다.

저자인 백기선 님도 다음에는 자신의 컴퓨터의 maven repository 를 통째로 압축해서 CD 에 담아서 배포하겠다고 할 정도였다.


Error - Can not find the tag library descriptor for "http://www.springframework.org/tags/form"



Can not find the tag library descriptor for "http://www.springframework.org/tags/form"


<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>


이 에러는 치명적인 것처럼 보이지만, 실행에는 문제가 없다.

이 에러는 소스의 문제가 아니라 이클립스의 버그인 것 같다.

왜냐하면 spring의 tag library 가 spring-webmvc-3.0.5.RELEASE.jar 파일에 있는데, 이 파일을 project 의 WEB-INF/lib 폴더에 넣으면 에러가 사라지기 때문이다.




이 jar 파일은 Java Build Path 에서 Libraries 탭에 보면 Maven Dependencies 에 속해 있다.
그러면 이클립스에서 자동으로 인식해서 tag library를 찾아야 하는데, 꼭 WEB-INF/lib 에 있어야지만 된다니...


P.S.

모든 에러를 해결하고 나서 Tomcat 7 서버를 실행도 시켜본 후, /WEB-INF/lib 에서 spring-webmvc-3.0.5.RELEASE.jar 파일을 삭제하고 나서 clean 하고 다시 빌드해도 

Can not find the tag library descriptor for "http://www.springframework.org/tags/form"

라는 에러가 발생하지 않는다.

웹 서버가 실행되면서 Maven의 라이브러리가 /WEB/lib 폴더로 export 된 것으로 이클립스에서 인식한 것일까?



Error - Referenced file contains errors (http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd)



이 에러는 저자가 약간의 실수를 한듯 하다.

web.xml 파일에서 xml schema 정의한 부분이 좀 잘못 되었다.

그러나 web.xml 의 이런 XML 정의와 관련된 에러는 실행에는 문제가 없다.


이 에러의 해결책은 2가지다. 


<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

version="2.5"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd">


이렇게 되어 있는 부분을 다음과 같이 수정한다.


<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

version="2.4"

xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


아니면 다음과 같이 수정한다.


<web-app version="2.5"

xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">


version 을 2.5 로 하려면 j2ee 로 되어 있는 부분을 javaee 로 수정하면 된다.


[출처] http://stackoverflow.com/questions/13437569/what-does-this-web-xml-error-mean


web-app_2_5.xsd 파일을 다운로드 받아보면 상단에


<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"

   targetNamespace="http://java.sun.com/xml/ns/javaee"

   xmlns:javaee="http://java.sun.com/xml/ns/javaee"

   xmlns:xsd="http://www.w3.org/2001/XMLSchema"

   elementFormDefault="qualified"

   attributeFormDefault="unqualified"

   version="2.5">


이렇게 되어 있다.


참고로 Tomcat 7 은 Servlet 3.0 까지 지원하고, Tomcat 6 는 Servlet 2.5 까지 지원, Tomcat 5.5 는 Servlet 2.4 버전까지 지원한다고 한다. [출처] http://stackoverflow.com/questions/8400301/cout-unknown-tag


[참고] 서블릿 버전별 DTD - http://antop.tistory.com/145



Warning - Unknown tag (Spring Custom Tag 를 인식못하고 warning 이 나는 경우)




별로 대단한 것은 아니다. 단순히 무시해도 되는 Warning 이지만, 이 warning 을 없애려면


web.xml 에서 <welcome-file-list> 태그 뒤에


<jsp-config>

<taglib>

<taglib-uri>http://www.springframework.org/tags/form</taglib-uri>

<taglib-location>/WEB-INF/tlds/spring-form.tld</taglib-location>

</taglib>

</jsp-config>


를 갖다붙인다.


spring-form.tld 파일은 

C:\Users\{사용자명}\.m2\repository\org\springframework\spring-webmvc\3.0.5.RELEASE\spring-webmvc-3.0.5.RELEASE.jar 파일의 압축을 풀어보면 META-INF 폴더 안에 들어있다.


spring-form.tld 파일을 복사해서 내 프로젝트의 WEB-INF 밑에 web.xml 에서 정의된 경로에 갖다 놓지 않으면 warning 은 없어지지만 실행 시 에러가 나서 해당 페이지가 뜨지 않는다.


에러메시지: 

org.apache.jasper.JasperException: /WEB-INF/views/member/form.jsp (line: 3, column: 71) File "/WEB-INF/tlds/spring-form.tld" not found


[참고] No tag library could be found with this URI ( http://antop.tistory.com/146 )

Tomcat 7 서버에서는 web.xml 에 <taglib> 를 정의하지 않아도 별 문제가 없지만, 웹로직 서버에서는 에러가 난다는 내용이다.



실행 시 Error - javax.validation.ValidationException: Unable to find a default provider


Build Error 를 다 해결하고 나서 웹 브라우저로 접속 시 첫화면은 잘 뜬다.



그런데, 웹 브라우저에서 http://localhost:8080/app/member/form 로 접속 시 500번 에러가 나는 경우이다.



org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.validation.beanvalidation.LocalValidatorFactoryBean#0': Invocation of init method failed; nested exception is javax.validation.ValidationException: Unable to find a default provider


Error log 를 봐도 소스의 어디에서 이상이 있는지를 알 수 없다.


javax.validation.ValidationException: Unable to find a default provider 를 Google 에서 검색해보면

이 에러는 hibernate-validator 라는 jar 파일과 관련이 있다는 것을 알 수 있다.


[출처] http://blog.naver.com/muchine98/140161045048


위 블로그의 내용을 발췌해 보자면,


Spring MVC 에서 JSR 303 @Valid Annotation 을 사용하기 위해서는 아래와 같이 hibernate-validator 라이브러리를 포함시켜야 한다.


<dependency>

 <groupId>org.hibernate</groupId>

 <artifactId>hibernate-validator</artifactId>

 <version>4.2.0.Final</version>

</dependency>


이름은 Hibernate이지만 실제로 하는 역할은 Validation과 관련된 것 뿐이다. 만약 다른 javax.validation 패키지를 지원하는 라이브러리를 등록한다면, WAS 서버 기동 시javax.validation.ValidationException: Unable to find a default provider라는 에러가 발생한다.


이후 세팅은 아래 링크를 참조한다.

http://www.mkyong.com/spring-mvc/spring-3-mvc-and-jsr303-valid-example/


legacy-sample 프로젝트의 pom.xml 에서 hibernate-validator 으로 검색해 보면


<dependency>

            <groupId>org.hibernate</groupId>

            <artifactId>hibernate-validator</artifactId>

            <version>4.0.0.GA</version>

        </dependency>


이제 C:\Users\{사용자명}\.m2\repository\org\hibernate\hibernate-validator\4.0.0.GA 폴더로 가서

hibernate-validator-4.0.0.GA.jar 를 검사해보자.

SHA-1 로 checksum 을 구해봐도 hibernate-validator-4.0.0.GA.jar.sha1 의 내용과 같지 않고, 압축을 풀어보면 에러가 나면서 압축이 풀리지 않는 것을 알 수 있다.


이제 C:\Users\{사용자명}\.m2\repository\org\hibernate\hibernate-validator\4.0.0.GA 폴더를 지우고, 프로젝트를 clean 하고 다시 빌드해보자. 그러면 eclipse 에서 pom.xml 에 정의된 라이브러리가 없는 경우 자동으로 다시 다운로드 받는다.


이런 경우가 Maven 의 잘못된 라이브러리 다운로드 때문에 실행 시 알수 없는 에러가 나는 경우이다.

 

Posted by 1010
반응형
error code :
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile) on project bb: Compilation failure

원인(여러가지가 있음)
1. 메이븐이 꼬임
2. jar파일이 제대로 읽히지 않아 에러
3. jdk 경로가 잘못 잡힘
4. jee에 tools.jar 가 추가되어 있지 않음

대처법
1. sub project maven clean -> main project maven clean -> main project maven build(즉, 모든 Project의 Maven clean , Install, build)
(sub project가 없을 시에는 main project만 clean)
2. pom.xml에서 Run As -> Run Configurations… -> Resolve Workspace artifacts 체크
(이 경우 이미 install 된 파일이 local에 있다면 불가능(서브 프로젝트로 넣고 있는데 workspace를 계속 검사해 jar 파일을 찾기 때문))
3.  Preferences -> java -> Installed JREs -> jdk 선택 -> Edit -> Directory -> jre를 jdk 폴더 안에 있는것으로 변경
4. Properties -> java build path -> jre 선택 -> Execution environment의 Environments 버튼 선택 ->
Add Extenal JARs 버튼 선택 -> tools.jar가 있는 jdk 경로 선택 -> finish

 

출처 : http://blog.naver.com/my464?Redirect=Log&logNo=150177934109 

Posted by 1010
98..Etc/centos2014. 1. 3. 19:57
반응형

http://scotchblue.blogspot.kr/2012/08/centos-6x-nvidia-install-driver.html


centos 6.x nvidia install driver

1. www.nvidia.co.kr 에서 드라이버 다운

2.nouveau disable 시키기
etc/modprobe.d/disable-nouveau.conf 파일생성
내용 -> blacklist nouveau
            option nouveau modset=0

3.boot/grup/grup.conf 에디트 하기
내용 -> rdblacklist=nouveau 추가 하기
위치 -> kernel행 마지막에 넣어준다

NVIDIA~~.run 파일을 실행하기 위해서는 실행화일로 바꿔줘야 하기때문에
chmod a+x NVIDIA~~.run 이라고 실행한후에 ./NVIDIA~~.run 한다
64bit일 경우에는 설치도중 나오는 32bit는 깔지 않는다 깔아버리면 먹통되버린다.


Posted by 1010
반응형

Maven으로 구성한 웹 프로젝트가 있다. Eclipse에서 디버깅을 하기 위해 Tomcat과 같은 WAS 설정을 하고 프로젝트를 디플로이 해서 사용하고 있었다. 헌데.. 이 잘 되던 프로젝트가 다른 사람의 Eclipse로 옮기거나 Eclipse 설정을 다시 할 때 아래와 같은 알 수 없는 ClassNotFoundException을 찍 뱉으며 실행이 되지 않는 경우가 종종 있다.

아래 오류가 이상한건.. Maven dependency로 잡아준 라이브러리의 클래스가 존재하지 않는다는 어처구니 없는 메시지이기 때문이다. 분명히 이쁘게 잘 잡아준 라이브러리가 존재하지 않는다고 하니 멍하니 하늘만 쳐다볼 뿐.. (아래 오류는.. Maven으로 Spring 라이브러리를 잡아 주었는데도 불구하고 WAS가 그 라이브러리를 인식하지 못 해서 못 찾는 오류 내용이다.)

정보: Starting Servlet Engine: Apache Tomcat/6.0.20
2011. 2. 8 오후 1:45:20 org.apache.catalina.core.StandardContext listenerStart
심각: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1387)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1233)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3877)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4429)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:722)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:583)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)


구글링을 해도 돌아오는 대답은 WAS의 라이브러리 경로에 해당 라이브러리 jar 파일을 넣어주라는 것 뿐.. 하지만 그럴거면 왜 힘들게 Maven을 쓰노?? Maven으로 라이브러리 종속 관계를 편하게 설정하고 사용하기 위한 것 아닌고?? 미친척 하고 오늘 하루 종일 아무 일도 안 하고 구글링만 했다. 오오~~ 우연히 발견한 반짝반짝 빛이 나는 멋진 글!! TㅅT


해결 방법은 Eclipse 프로젝트의 .classpath 파일에 다음과 같이 적어주어서.. 프로젝트를 WAS로 디플로이 할 때 프로젝트의 라이브러리를 함께 사용할 수 있도록 설정해 주어야 한다는 것!! 굳이 내용을 살짝 살펴보면 Maven으로 추가한 라이브러리(org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER)를 /WEB-INF/lib 경로로 넣어서 WAS에 배포될 때 함께 사용할 수 있도록 한다.. 정도?? -ㅅ-;;

<classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER">
    <attributes>
        <attribute name="org.eclipse.jst.component.dependency" value="/WEB-INF/lib"/>
    </attributes>
</classpathentry>



.classpath 파일은 workspace 경로에 해당 프로젝트 폴더 최상위에 존재한다. Eclipse에서는 Package Explorer나 Project Explorer에서는 숨겨져 있어 보이지 않고 Navigator view를 사용하면 볼 수 있다.


 




이 문제로 매번 고생하면서 오늘도 몇 시간을 날렸는데.. 답은 의외로 너무 간단하게 1줄.. 아.. 죽갔네.. 이런 문제 젤 시러.. -ㅅ-;;




수정 덧)
권남님의 덧글로 더 쉽게 설정하는 방법을 알았다. 정말 감사합니다~~
프로젝트를 오른쪽 클릭하고 "Properties"를 선택하여 Properties 창을 연다. 그리고 왼쪽 메뉴(?)에서 "Deployment Assembly"를 선택하면, WAS에 배포될 때 함께 사용될 녀석들을 관리할 수 있다. Maven Dependency로 걸린 녀석들을 라이브러리로 추가하기 위해 "Java Build Path Entries"를 선택한다.



그리고 "Maven Dependencies"를 선택하면 끝~~



이렇게 설정되면 .classpath 파일에 위에 쓰여진 내용이 자동으로 설정된다. 으흐~~ 쉽다~~ ㅋㅋ

Posted by 1010
98..Etc/centos2014. 1. 3. 13:57
반응형
http://efficient.tistory.com/entry/centos-ftp설치-및-설정
http://efficient.tistory.com/category/Linux
Posted by 1010
반응형

출처 : http://donzbox.tistory.com/478

# 증상 : Eclipse 강제종료 후 Eclipse 실행시 아래와 같은 alert 메시지 발생 후 실행이 안됨

alert Message :


.log Error Message :

!ENTRY org.eclipse.core.resources 2 10035 2013-05-30 16:23:36.686
!MESSAGE The workspace exited with unsaved changes in the previous session; refreshing workspace to recover changes.

!ENTRY jp.gr.java_conf.ussiy.app.propedit 1 0 2013-05-30 16:23:39.556
!MESSAGE properties loading 'DevWork'

!ENTRY jp.gr.java_conf.ussiy.app.propedit 1 0 2013-05-30 16:23:39.566
!MESSAGE properties loading 'RemoteSystemsTempFiles'

!ENTRY jp.gr.java_conf.ussiy.app.propedit 1 0 2013-05-30 16:23:39.566
!MESSAGE properties loading 'Servers'

!ENTRY jp.gr.java_conf.ussiy.app.propedit 1 0 2013-05-30 16:23:39.566
!MESSAGE properties loading 'htmlWork'

!ENTRY jp.gr.java_conf.ussiy.app.propedit 1 0 2013-05-30 16:23:39.566
!MESSAGE properties loading 'kofia_user'


# 해결 :

%ECLIPSE_HOME%workspace\.metadata\.plugins\org.eclipse.e4.workbench\workbench.xmi 파일 삭제

(workbench.xmi 기존 작업에 대한 세팅이 저장되어 있는 File )


참고 사이트 :

ApexSoft DevArtMent

URL : http://apexsoftdevartment.blogspot.kr/2012/11/blog-post.html

 

Posted by 1010
98..Etc/centos2014. 1. 3. 12:34
반응형

OS : CentOS 6.2

apache : 2.4.1

tomcat : 6.0 (6.9.35) (Binary)


1. tomcat 다운로드 & 압축해제

[root@CentOS ~]# cd /usr/local

[root@CentOS local]# wget http://mirror.apache-kr.org/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz

[root@CentOS local]# tar xvfz apache-tomcat-6.0.35.tar.gz

[root@CentOS local]# ln -s apache-tomcat-6.0.35.tar.gz tomcat


http://www.apache.org/dist/tomcat/tomcat-connectors/jk/tomcat-connectors-1.2.35-src.tar.gz

Posted by 1010
98..Etc/centos2014. 1. 3. 12:32
반응형

1) Java SE Development Kit 6(JDK6) download

http://www.oracle.com/technetwork/java/javase/downloads/jdk-6u31-download-1501634.html 

Linux x86(32-bit) jdk-6u31-linux-i586.bin 파일

 

2) 설치

mv /home/navi/Downloads/jdk-6u31-linux-i586.bin /usr/local  -> 파일 이동

cd /usr/local -> 디렉토리 변경

chmod 755 jdk-6u31-linux-i586.bin -> 파일 실행 권한 변경

./jdk-6u31-linux-i586.bin -> 설치

 

3) 환경변수 설정

ln -s jdk1.6.0_31 java -> java로 링크 설정

gedit /etc/profile -> 환경변수 설정 열기

export JAVA_HOME=/usr/local/java
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=$JAVA_HOME/jre/bin/lib/ext:$JAVA_HOME/lib/tools.jar

 

source /etc/profile -> 환경 변수 적용

 

env -> 설정한 환경변수 확인

 

java -version -> 최종적으로 java 설치 완료 확인(아래와 같이 나오면 정상 설치)

java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

 

4) openjdk 삭제 or java 순서 바꾸기

만약 자동적으로 openjdk가 설치되어 있다면, 새로 sun jdk설치 하였다고 하더라도

버전을 출력해보면...

#java -version

java version "1.6.0_22"
OpenJDK Runtime Environment (IcedTea6 1.10.6) (rhel-1.43.1.10.6.el6_2-i386)
OpenJDK Server VM (build 20.0-b11, mixed mode

위와 같이 openjdk가 기본적으로 딱 자리잡고 있다.

그러므로 이런경우 openjdk를 삭제하던 아니면 java config를 수정해서 기본 java path를

설치한 path로 잡아주어야 한다.


***삭제의 경우

rpm -qa | grep jdk

java-1.6.0-openjdk-devel-1.6.0.0-1.43.1.10.6.el6_2.i686
java-1.6.0-openjdk-1.6.0.0-1.43.1.10.6.el6_2.i686
java-1.6.0-openjdk-javadoc-1.6.0.0-1.41.1.10.4.el6.i686

yum remove java-1.6.0-openjdk-devel java-1.6.0-openjdk java-1.6.0-openjdk-javadoc


***그냥 두고 jdk 버전의 순서를 변경하는 경우

우선 현재 java config를 살펴보자

# update-alternatives --config java
3 개의 프로그램이 'java'를 제공합니다.
  선택    명령
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.6.0-openjdk/bin/java
   2           /usr/lib/jvm/jre-1.5.0-gcj/bin/java

자동적으로 설치된 openjdk가 자리잡고 있다.

그러므로, 위에 설치한 버전을 config에 추가하고 선택해주어야 한다.

우선 config에 추가

#update-alternatives --install "/usr/bin/java" "java" "/usr/local/jdk1.6.0_31/bin/java" <= 새로 설치한 path

그럼 추가가 됐는지 볼까요?

# update-alternatives --config java
3 개의 프로그램이 'java'를 제공합니다.
  선택    명령
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.6.0-openjdk/bin/java
   2           /usr/lib/jvm/jre-1.5.0-gcj/bin/java
   3           /usr/local/jdk1.6.0_31/bin/java


 현재 선택[+]을 유지하려면 엔터키를 누르고, 아니면 선택 번호를 입력하십시오 :

3)에 추가가됐네요! 그럼 3을 입력하고 엔터키...

# update-alternatives --config java

3 개의 프로그램이 'java'를 제공합니다.
  선택    명령
-----------------------------------------------
* 1           /usr/lib/jvm/jre-1.6.0-openjdk/bin/java
   2           /usr/lib/jvm/jre-1.5.0-gcj/bin/java
  +3           /usr/local/jdk1.6.0_31/bin/java


3이 선택된 것을 알 수 있습니다.

그리고 버전을 출력하면,

#java -version

Java version "1.6.0_31"
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) Server VM (build 20.6-b01, mixed mode)

위와 같이 위에서 설치한 sun Java가 잡혀있는 것을 볼 수 있다.

 

Posted by 1010