반응형

참고

http://java.sun.com/products/javamail/index.jsp
http://java.sun.com/javase/technologies/desktop/javabeans/glasgow/jaf.html
http://java.sun.com/developer/onlineTraining/JavaMail/contents.html

http://java.sun.com/products/javamail/javadocs/index.html
관련 시리즈
http://javacan.madvirus.net/main/search/search.tle?type=all&keyword=mail

주로 작성된 메일을 송신하고 수신하는 API를 제공하는 것이다. 이러한 기능은

메일 서버의 역할과는 구별되는 것이다.


메일 서버는 송신된 메일을 받아들이고 메일 내용을 저장하거나 재전송을 하는  Agent 역할을 한다.


JavaMail Package는 메일 서버에게 메일을 전송하고 메일 서버에 저장된 메일을 수신하는데 유용한 클레스 및 API를 제공한다.


ex) MicroSoft사의 OutLook


 JavaMail API를 구조적으로 살펴보면 우선 메시지 구성, 저장, 수신 구조를 추상화해놓은absctruct layer가 있다. 이러한 추상 계층의 목적은  특정 프로토콜과는 상관없이 메일 구조 체계를 제공하겠다는 목적이다. 즉 인터넷 메일 뿐만 아니라 모바일이나 또 다른 메일 체계에도 적용되는 구조를 제공하는 것이다.


 이러한 추상 계층 이외에 JavaMail API는 가장 흔히 사용하는 인터넷 메일을 위한 클레스 및 API를 제공한다. 이 클레스들은 기본적인 abstruct layer를 인터넷 메일이하는 측면에서 구현 즉 implementation한 클레스들이다. JavaMail API를 가지고 직접적으로 응용프로그램을 작성할 수 있는 방법은 이 인터넷 메일 구현 계층을 사용하는 것이다.


인터넷 메일에 대하여 잘 이해하려면 우선 인터넷 메일 시스템에서 사용되는 통신 프로토콜 표준에 대하여 이해할 필요가 있다. 이러한 프로토콜들은 IETF(Internet Engineering Task Force)에 의하여

소정의 절차를 거친 후 RFC라는 형태로 발표되는데 인터넷 메일과 관련된 주요 표준 프로토콜은

SMTP, POP3, IMAP, MIME 등이 있다. (http://www.ietf.org/)


- SMTP(Simple Mail Transfer Protocol)

RFC 0788에서 정의 되었는데 이후에도 많은 수정이 있다.

SMTP는 기본적으로 인터넷 메일을 서버로 송신하고 서버가 이 메일을 수신하는데 필요한 커멘트 및 표준 절차등을 정의하고 있다.

Java Mail API의 주요 구성 요소

이 글에서는 Java Mail API의 모든 클래스에 대해서 알아보지는 않을 것이며, 가장 중심적인 역할을 하는 클래스인 javax.mail.Session, javax.mail.Store, javax.mail.Transport, javax.mail.Folder, javax.mail.Message 클래스에 대해서 알아볼 것이다. 실제로 이 다섯개의 클래스만 알맞게 사용하면 매우 손쉽게 메일 시스템을 구축할 수 있다.

javax.mail.Session

javax.mail.Session은 Java Mail API를 사용하는 출발점이 되는 클래스로서 다양한 메일 프로토콜을 위한 서비스 프로바이더 구현(Service Provider Implementation; SPI)을 나타내는 클래스를 로딩하고 제어할 수 있는 메소드를 제공하고 있다. 예를 들어, javax.mail.Store 클래스의 인스턴스는 javax.mail.Session 클래스를 통해서 구할 수 있다. (여기서 서비스 프로바이더는 Java Mail API를 이용하여 구현 클래스 계층을 제공하는 개발자는 벤더를 의미한다. 현재 Java Mail API는 IMAP, SMTP, POP3 프로토콜에 대한 구현 계층을 제공하고 있다.)

javax.mail.Store

javax.mail.Store 클래스는 특정한 메일 프로토콜을 이용하여 메일의 읽기, 쓰기, 감시, 검색 등을 할 수 있도록 해 준다. Session 클래스를 사용하여 구할 수 있으며, 메일 저장소를 추상화한 javax.mail.Folder에 접근할 수 있도록 해 준다. 서비스 프로바이더는 Store 클래스를 알맞게 구현해야 한다.

javax.mail.Folder

javax.mail.Folder 클래스는 메일 메세지에 계층적 구조를 제공하며 메일 메세지에 접근할 수 있도록 해 준다. 메일 메세지는 javax.mail.Message 클래스의 객체로 표현된다. 서비스 프로바디어는 Folder 클래스를 알맞게 구현해야 한다.

javax.mail.Transport

javax.mail.Transport 클래스는 특정한 프로토콜을 사용하여 메세지를 전송할 때 사용된다. 서비스 프로바이더는 이 클래스를 알맞게 구현해야 한다.

javax.mail.Message

javax.mail.Message 클래스는 주제, 수신자의 이메일주소, 발송자의 이메일 주소, 보낸 날짜와 같은 실제 이메일 메세지의 세부 사항을 나타낸다. 서비스 프로바이더는 자신이 사용하는 프로토콜에 알맞게 Message를 구현해야 한다.

Java Mail API와 JAF

Java Mail API는 메시지에 다양한 포맷을 사용할 수 있도록 하기 위해 JAF를 사용한다. JAF는 별도의 확장 API로서 다양한 데이터 형식을 이용하여 작업하는 방법을 통합하기 위해 작성되었다. 실제로 JAF를 이용하면 간단한 텍스트 데이터에서부터 이미지, 비디오 등의 매우 복잡한 멀티 미디어 데이터로 구성된 문서까지 다양한 데이터를 제공할 수 있다.

반응형
Enumeration : 자바 초기버젼에서 개발된 것으로, HashTable과 Vector에서 사용가능
Iterator : jdk 1.2버젼에서 개발된 것으로, Collection 인터페이스를 구현한 모든 클래스에서 사용가능.

API를 확인해볼까요??
Vector와 HashTable에 있는 메소드입니다.
사용자 삽입 이미지
(HashTable에는 Enumeration 타입의 key()메소드도 존재합니다.)

하지만, ArrayList, LinkedList, HashMap에서는 찾아볼 수 없었습니다.
iterator는 HashTable이든 HashMap이든, Vector든 LinkedList든,
모두 쓸 수 있다는 것이죠.

Iterator에 대해서는 저번에 알아봤으니까.
이번에는 Enumeration의 API를 간단하게 뒤져봐야겠네요.

hasMoreElements

boolean hasMoreElements()
Tests if this enumeration contains more elements.
Returns:
true if and only if this enumeration object contains at least one more element to provide; false otherwise
Iterator의 hasNext()와 똑같은 일을 하는군요!!

nextElement

E nextElement()
Returns the next element of this enumeration if this enumeration object has at least one more element to provide.
Returns:
the next element of this enumeration.
Throws:
NoSuchElementException - if no more elements exist.
다음 elements가 존재하지 않으면, Exception이 발생하네요 ㅋㅋ
이것 또한 Iterator의 next()와 같아 보이는군요..ㅋㅋ

Vector의 요소들을 출력하는 예제입니다.
for (Enumeration e = v.elements() ; e.hasMoreElements() ;) {
System.out.println(e.nextElement());

}
자 그럼, 본격적으로 차이점에 대해서 알아보도록 합시다.

Snap Shot
Enumeration과 Iterator의 가장 큰 차이점은 바로 Snap Shot입니다.
Enumeration은 Snap Shot을 지원합니다. 여기서 'fail-fast
'라는 개념이 나오는데요.
Iterator는 fail-fast 방식이라고 합니다.
그럼 과연 이것이 무엇일까요?? 좋은 아티클이 있어서 긁어왔습니다...-ㅅ-;;

Fail-Fast

자바 2 이전 버전에서 사용되던 Vector, Hashtable의 뷰 객체인 Enumeration은 fail-fast 방식이 아니었으나, 자바 2의 콜렉션 프레임워크에서 콜렉션 뷰인 Iterator, ListIterator 객체는 fail-fast 방식이라는 점이다.

콜렉션 뷰는 콜렉션 객체에 저장된 객체들에 대한 순차적 접근을 제공한다. 그러나, 뷰 객체인 Enumeration 또는 Iterator 객체를 얻고 나서 순차적 접근이 끝나기 전에 뷰 객체를 얻은 하부 콜렉션 객체에 변경이 일어날 경우, 순차적 접근에 실패하게 된다. 여기서 변경이라는 것은 콜렉션에 객체가 추가되거나 제거되는 것과 같이 콜렉션 구조의 변경이 일어나는 경우를 말한다.

이런 상황은 멀티쓰레드 구조와 이벤트 구동 모델에서 일어날 수 있으며, 개발자가 혼자 테스트할 경우 발견하기 어려운 문제이다. 따라서 정확한 이해와 예상이 필요하며, 이에 대한 대처 방안을 마련해야 한다.

하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 Enumeration 객체는 실패를 무시하고 순차적 접근을 끝까지 제공한다. Iterator 객체는 하부 콜렉션 객체에 변경이 일어나 순차적 접근에 실패하면 ConcurrentModificationException 예외를 발생한다. 이처럼 순차적 접근에 실패하면 예외를 발생하도록 되어 있는 방식을 fail-fast라고 한다.

Iterator는 fail-fast 방식으로 하부 콜렉션에 변경이 발생했을 경우, 신속하고 결함이 없는 상태를 만들기 위해 Iterator의 탐색을 실패한 것으로 하여 예외를 발생하며, 이렇게 함으로써 안전하지 않을지도 모르는 행위를 수행하는 위험을 막는다. 왜냐하면 이러한 위험은 실행 중 불특정한 시간에 멋대로 결정되지 않은 행위를 할 가능성이 있기 때문에 안전하지 않다고 할 수 있기 때문이다.


멋집니다..-ㅅ-; 더이상 설명할 것이 없을 것 같은데요.
흐음.. 일단 정리를 해보면, iterator는 일종의 pointer가 있습니다.
예전에 LinkedList 개념을 그림으로 그려보았는데요.
그거랑 매우 비슷합니다.

사용자 삽입 이미지

포인터가 Array의 자기자신과 다음 객체를 가르키는 것이지요.
그래서 위에 아티클에 써져있는 것처럼 삭제나 수정시 참조무결성 원칙에 어긋나게되어서
에러가 발생하게 되는것입니다.

아티클에 SnapShot에 대한 좋은 내용도 있더군요.

스냅샷

이처럼 콜렉션 뷰 객체인 Iterator와 ListIterator 객체가 fail-fast 방식인 이유는 스냅샷에 대한 보장을 포함하고 있지 않기 때문이다. 즉, 콜렉션의 뷰인 Iterator 객체를 얻었을 때 그 순간의 상태를 따로 생성하지 않기 때문에, Iterator 객체에서 순차적으로 하부 콜렉션 객체를 접근할 때, 콜렉션 객체의 변경에 영향을 받게 된다.

따라서, 콜렉션 뷰인 Iterator를 통하여 순차적 접근을 행하는 도중에 하부 콜렉션 객체에 변경이 일어날 가능성이 있는 경우 스냅샷을 명시적으로 생성하는 것이 좋다. ArrayList를 이용한 스냅샷을 생성하는 간단한 방법은 다음과 같다.

public Iterator snapshotIterator(Collection collection) {
return new ArrayList(collection).iterator();
}

이렇게 스냅샷을 이용하면 스냅샷이 생성된 순간의 상태에 대하여 Iterator를 통한
콜렉션의 뷰를 생성하게 된다. 하부 콜렉션의 변경이 일어나도 스냅샷에는 반영되지
않으며 Iterator는 실패없이 실행된다.

콜렉션 계열 중에 ArrayList를 사용하는 이유는 다른 콜렉션 계열 클래스들보다 생성과순차적 접근이 빠르기 때문이다. ArrayList가 삽입과 삭제에 불리한 단점이 있으나
이 경우에는 삽입과 삭제가 없으므로 고려되지 않는다.


조금 더 쉽게 정리를 해보자면,
Enumeration은 객체를 복사해서 저장합니다. 그래서 삭제나 수정시 전체 콜렉션에는
큰 타격을 주지 않는다는 것이죠.

Synchronization

제가 원래 알고 있던 지식은 "Enumeration은 동기화를 지원한다." 였는데,
어디서 잘 못 주워들었나봅니다. -ㅛ-
아티클에 <<콜렉션 프레임워크의 클래스들은 동기화를 보장하고 있지 않다.>>
라고 써져있더라구요. 그래서 찾아보았습니다. 역시 API에서 Enumeration부분을 봐도,
동기화에 대해서 정의된 부분은 찾을 수 없더라구요.
하지만,  Vector와 ArrayList의 차이점을 공부한 글에서도 알 수 있을 듯이
Vector는 동기화가 아주 넘쳐납니다-ㅅ-; 그러한 사실은 이클립스에서 Vector.class를
가보면, synchronized로 도배가 되어있는 것을 볼 수 있답니다.

아티클에는,
<<멀티쓰레드에서 콜렉션 구현 객체에 동시에 추가, 삭제를 위한 접근이 행해진다면, 그러한 행위가 발생하는 부분에 동기화를 명시적으로 해주어야 한다.>>
라고 써져있더군요. 방법까지 친절하게 명시해주고 있었습니다.
요렇게-ㅅ-;

Collection c = Collections.synchronizedCollection(myCollection);

결론적으로, Enumeration과 Iterator 사이에서 뚜렷하게 차이를 보이는 점은
 "스냅샷을 지원하느냐 하지 않느냐" 라는 것 같습니다.
아티클에서는 "Fail-fast Iterator에 대한 멀티쓰레드 무결성 해결방법" 이라는 제목아래 글을 써내려가고 있는데요. 스냅샷과 동기화가 Iterator의 멀티쓰레드 무결성 해결방법으로 제시되었고, 그와 함께 예시 소스코드도 보여주고 있습니다. 그러니까 Iterator가 스냅샷을 지원하지 않아도, 스냅샷을 명시적으로 생성할 수 있다는 것입니다. 이런건 한번 소스코드를 작성해봐야 확실히 알 수 있겠네요. 일단 이번 포스팅은 여기서 마치고, 소스코딩을 야심차게 시작해보도록 하겠습니다.-ㅛ-; 아자 +ㅅ+ ㅋ
< 출처: http://happystory.tistory.com >
반응형
Vector or ArrayList -- which is better and why?

Sometimes Vector is better; sometimes ArrayList is better; sometimes you don't want to use either. I hope you weren't looking for an easy answer because the answer depends upon what you are doing. There are four factors to consider:

Vector가 더 나을 때도 있고, ArrayList가 더 나을 때도 있다. 또는, 둘 다 사용하길 원하지 않을 수도 있다. 나는 당신이 쉬운 답을 찾으려 하지 않기를 바란다. 왜냐하면 답은 당신이 하는 방식에 따라 달라질 테니까 말이다. 고려해야 할 다음 4가지 요인이 있다.

API
In The Java Programming Language (Addison-Wesley, June 2000) Ken Arnold, James Gosling, and David Holmes describe the Vector as an analog to the ArrayList. So, from an API perspective, the two classes are very similar. However, there are still some major differences between the two classes.

The Java Programming Language 에 Ken Arnold, James Gosling, and David Holmes는 Vector를 ArrayList의 상사형(서로 형이 같은)이라고 묘사한다. 그래서 API를 보면 두 개의 클래스는 매우 유사하다. 하지만 여전히 두 클래스 사이에는 중요한 차이점이 존재한다.

Synchronization
Vectors are synchronized. Any method that touches the Vector's contents is thread safe. ArrayList, on the other hand, is unsynchronized, making them, therefore, not thread safe. With that difference in mind, using synchronization will incur a performance hit. So if you don't need a thread-safe collection, use the ArrayList. Why pay the price of synchronization unnecessarily?

Vector는 동기화 되어있다. Vector의 내용에 접근하는 어떤 메소드는 thread safe1 하다. 반면에ArrayList는 동기화 되어있지 않다. 그러므로 ArrayList로 만들어진 것은 thread safe하지 않다. thread safe가 필요하지 않는데도 동기화를 사용하면, performance hit2 을 초래할 것이다. 만약 당신이 thread-safe 컬렉션을 필요로 하지 않는다면, ArrayList를 사용하라. 왜 불필요하게 synchronization의 비용을 지불하려 하는가?

Data growth
Internally, both the ArrayList and Vector hold onto their contents using an Array. You need to keep this fact in mind while using either in your programs. When you insert an element into an ArrayList or a Vector, the object will need to expand its internal array if it runs out of room. A Vector defaults to doubling the size of its array, while the ArrayList increases its array size by 50 percent. Depending on how you use these classes, you could end up taking a large performance hit while adding new elements. It's always best to set the object's initial capacity to the largest capacity that your program will need. By carefully setting the capacity, you can avoid paying the penalty needed to resize the internal array later. If you don't know how much data you'll have, but you do know the rate at which it grows, Vector does possess a slight advantage since you can set the increment value.

내부적으로 ArrayList와 Vector둘 다 Array를 사용하면서 그들의 컨텐츠를 유지한다. 당신은 당신의 프로그램에서 둘 중에 하나를 사용하는 동안에 이 사실을 명심할 필요가 있다. ArrayList 또는 Vector의 요소를 삽입할 때, 만약 그 용량을 다 써버린다면, object는 자신의 내부array를 확장시킬 필요가 있다. Vector는 자신의 array 크기의 배수만큼 초기화 한다. 반면에 ArrayList는 자신의 array 크기의 50%만큼 씩 증가시킨다. 당신이 이러한 클래스들을 사용하는데 의존한다면, 당신은 새로운 요소들을 추가하는 동안에 결국 대량의 performance hit을 초래할 것이다. 당신의 프로그램이 필요로 하는 최대의 용량만큼 객체의 초기화 용량을 정하는 것이 최선이다. 신중하게 용량을 셋팅해라. 그러면, 당신은 후에 내부 array를 resize하는데 필요한 요금 지불을 피할 있다. 만약 당신이 얼마나 데이터를 가지게 될지는 모른지만, 그것이 증가하는 비율을 안다면 , Vector는 당신이 증가값을 정할 수 있기 때문에 근소한 이점을 가진다.(아래 글 참조)

Usage patterns
Both the ArrayList and Vector are good for retrieving elements from a specific position in the container or for adding and removing elements from the end of the container. All of these operations can be performed in constant time -- O(1). However, adding and removing elements from any other position proves more expensive -- linear to be exact: O(n-i), where n is the number of elements and i is the index of the element added or removed. These operations are more expensive because you have to shift all elements at index i and higher over by one element. So what does this all mean?

ArrayList와 Vector 둘 다 컨테이너의 특정 위치에서 요소들을 되찾거나, 컨테이너의 끝에서 요소들을 더하거나, 삭제할때 유용하다. 이러한 모든 기능은 constant time-- O(1)(맨 아래 글을 참조하세요.)에서 수행될 수 있다. 하지만, 어떤 다른 자리에서 요소들을 더하거나 제거하는 것은 더 많은 비용을 발생시킨다. 정확하게 직선위에서..-ㅅ-?? n은 요소들의 수이고, i는 더해지거나 제거되는 요소의 인덱스다. 당신이 하나의 요소에 때문에 모든 요소들을 index i에 그 이상의 값으로 옮겨야만 하기때문에 이러한 operation들은 더 많은 비용이 든다. 그렇다면 이러한 것은 무엇을 의미하는가?

It means that if you want to index elements or add and remove elements at the end of the array, use either a Vector or an ArrayList. If you want to do anything else to the contents, go find yourself another container class. For example, the LinkedList can add or remove an element at any position in constant time -- O(1). However, indexing an element is a bit slower -- O(i) where i is the index of the element. Traversing an ArrayList is also easier since you can simply use an index instead of having to create an iterator. The LinkedList also creates an internal object for each element inserted. So you have to be aware of the extra garbage being created.

그것은 만약 당신이 요소들을 검색하거나, Array의 끝에서 요소들을 더하거나 제거하기를 원한다면 Vector나 ArrayList 둘 중에 하나를 사용하라는 것을 의미한다. 만약 당신이 그 밖의 무엇인가를 하길 원한다면, 스스로 또다른 콘테이너 클래스를 찾아라. 예를들어, LikedList는 constant time -- O(1) 에서 어디서든지 요소를 더하거나 제거할 수 있다. 하지만, 요소를 찾는 것은 조금 느리다. ArrayList을 왔다갔다 하는 것은 또한 당신이 iterator을 만들어야만 하는 대신에 인덱스를 더욱 간편하게 사용할 수 있기 때문에 더 쉽다. LinkedList는 또한 삽인된 각각의 요소에 대한 내부 객체를 만든다. 그래서 당신은 만들어지는 여분의 garbage에 대해서 알아야만 한다.

Finally, in "PRAXIS 41" from Practical Java (Addison-Wesley, Feb. 2000) Peter Haggar suggests that you use a plain old array in place of either Vector or ArrayList -- especially for performance-critical code. By using an array you can avoid synchronization, extra method calls, and suboptimal resizing. You just pay the cost of extra development time.

마지막으로 Practical Java "PRAXIS 41"에 Peter Haggar은 당신이 Vector 나 ArrayList 둘 중의 하나 대신에 명백한 old array를 사용할 것을 제안한다. -특히 비판적인 코드에서??? array를 사용함으로써, 당신은 동기화, 여분의 메소드를 부르는 것. 그리고 차선의 resizing을 피할 수 있다. 당신은 단지 여분의 개발시간비용만을 지불할 것이다.

어허...ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 해석하는데 무지 오래걸렸습니다..-ㅅ-
하지만 해놓고보니.. 뿌듯하군요.
그러니까, ArrayList와 Vector의 가장 큰 차이점으로 꼽을 수 있는 것은 "동기화"입니다.
하지만, 여러 문서를 찾아보니 ArrayList가 동기화되는 Vector보다 퍼포먼스가 좋다는
이야기가 많더라구요. 즉, 적절한 곳에 ArrayList를 동기화 해서 사용하는게 더 좋다는 의견이
많았습니다. 저는 아직 Vector를 한번 더 사용해 본적도 없고, 개발 경험이 거의 전무해서
잘 와닿지는 않고, 공감할래야 공감할 수도 없지만... 이해했다는 것에 만족해야겠습니다.
-ㅅ-... 화잇힝...ㅋㅋ

Vector - Data Growth 추가사항(API문서)
The Vector class implements a growable array of objects. Like an array, it contains components that can be accessed using an integer index. However, the size of a Vector can grow or shrink as needed to accommodate adding and removing items after the Vector has been created.

Vector 클래스는, 오브젝트의 가변장 배열을 구현합니다. 여기에는 배열과 같이, 정수 인덱스를 사용해 액세스 할 수 있는 요소가 격납되고 있습니다. 그러나, Vector 의 사이즈는 작성 후에 추가 및 삭제된 객체를 격납할 수 있듯이 필요에 따라서 늘리거나 줄이거나 할 수가 있습니다.

Each vector tries to optimize storage management by maintaining a capacity and a capacityIncrement. The capacity is always at least as large as the vector size; it is usually larger because as components are added to the vector, the vector's storage increases in chunks the size of capacityIncrement. An application can increase the capacity of a vector before inserting a large number of components; this reduces the amount of incremental reallocation.

각 Vector 는,capacity (용량)와 capacityIncrement (증가량)를 관리하는 것으로써 기억 관리를 최적화하려고 합니다.capacity 는 항상 Vector 의 요소수에 가까운 값이며, 통상은 요소수부터 커집니다. 이것은 Vector 에 요소가 더해질 때, Vector 의 기억 영역은 capacityIncrement 만 늘려지기 때문입니다. 많은 요소를 삽입하기 전에 애플리케이션으로 용량을 필요한 값으로 설정해 두면, 메모리의 재배분의 회수를 줄일 수가 있습니다.


Constant Time : TimeComplexity 단위의 하나로, 입력 n의 크기의 증감과 상관 없이 주어진 문제를 푸는데에 걸리는 시간(See DiscreteTime)이 동일한 것을 말하며, BigONotation으로는 O(1)이라고 표시합니다. 예를 들어 배열의 i번째 원소를 가져오는 연산은 배열의 크기 n과 무관하게 동일한 속도로 수행됩니다.
TimeComplexity : 시간 복잡도. Automaton으로 어떠한 문제를 풀기 위해 소모되는 이산시간(See DiscreteTime)의 크기를 말합니다.
Automaton : 물리적/논리적인 자동기계-ㅅ-.. 라는데... 그냥 컴퓨터로 이해하겠습니다.
BigONotation : 아.. 이건 진짜 모르겠습니다-ㅅ-;;;;;;;;;;;;;;;;;;;;;;; 살려주세요 ㅠ
출처 : http://jania.pe.kr/wiki/jwiki/moin.cgi

+ Recent posts