- notice 스킨변경~
[JAVA]검색결과, 72건
[Flex] Flash Socket 이용 시 소켓 보안문제인 <policy-file-request/> 문제 해결방법-_-
Posted in 플렉스(Flex) // Posted at 2010/07/25 23:04일단, Flash에서 CrossDomain에 걸리는 데이터를 요청할 때 Plicy File인 crossdomain.xml 파일을 root에 정의해둬서 해당 도메인이면 데이터를 허용하게 할 수 있습니다.
소켓도 마찬가지입니다. 해당 도메인에서 해당 포트로 들어온 요청은 받겠다는 정책파일을 작성할 수 있습니다.
최초 소켓이 정책파일을 요청하게 되는 포트는 843포트입니다. 만약 이포트가 열러있지 않다면 현재 연결하려고 하는 포트로 <policy-file-request/>를 날려서 정책파일을 요청하게 됩니다.
그러면 843이든, 해당포트든 간에 정책파일만 날려주면 됩니다.
<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
<allow-access-from domain="*" to-ports="10000-10001" />
</cross-domain-policy>소켓에 대한 정책파일을 정의한 것인데, domain에는 허용할 도메인을 쓰고, to-ports에는 허용할 포트를 쓰면 됩니다.
이걸 날려주면 이제 연결할 포트로부터 데이터를 주고 받을 수 있습니다.
초간단 에코예제!
일단 서버는 자바로...(그나마 자신있는 언어라서ㅠㅠ)
일단 PlicyFileServer를 하나 돌릴 쓰레드를 만듭니다.
PlicyFileServer.java
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class PolicyFileServer extends Thread{
private ServerSocket serverSocket;
private String policyFile = "<?xml version='1.0'?>" +
"<!DOCTYPE cross-domain-policy SYSTEM '/xml/dtds/cross-domain-policy.dtd'>" +
"<cross-domain-policy>" +
"<allow-access-from domain='*' to-ports='10000' />" +
"</cross-domain-policy>";
@Override
public void run() {
try {
serverSocket = new ServerSocket(843);
while (true) {
final Socket socket = serverSocket.accept();
new Runnable() {
@Override
public void run() {
try {
socket.setSoTimeout(10000);
InputStream in = socket.getInputStream();
byte[] buffer = new byte[23];
if ( in.read(buffer) != -1 && (new String(buffer)).startsWith("<policy-file-request/>") ) {
OutputStream out = socket.getOutputStream();
out.write(policyFile.getBytes());
out.write(0x00);
out.flush();
out.close();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try { socket.close();} catch(Exception ex){}
}
}
}.run();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
내용을 보면 그냥 서버소켓하나 만들어서 요청이 들어오면 그 소켓으로 policy file을 전송하는 형태입니다. 파일은 만들기 귀찮아서-_- 그냥 String으로 선언-_- 보면 모든도메인에 한해서 10000포트를 열어주는 겁니다.
이제 메인서버!
SocketTest.java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTest {
private ServerSocket server;
public SocketTest() {
try{
server = new ServerSocket(10000);
System.out.println("접속을 기다립니다.");
while (true){
final Socket socket = server.accept();
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
PrintWriter pw = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println("수신데이터 : " + line);
pw.println("Hello! " + line);
pw.flush();
}
} catch (Exception e) {
try { if(socket != null) socket.close(); } catch (Exception ex) {}
}
}
});
t.start();
}
} catch(Exception e){
System.out.println("Error!");
}
}
public static void main(String[] args) {
new PolicyFileServer().start();
new SocketTest();
}
}데이터를 받으면 다시 Hello!를 붙여서 다시 전송해주는 echo서버를 하나 만듭니다.
그리고 main함수에서는 PolicyFileServer쓰레드를 하나 시작하고, EchoServer를 돌립니다.
이제 Flex!
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600"
applicationComplete="application1_applicationCompleteHandler(event)">
<fx:Script>
<![CDATA[
import mx.events.FlexEvent;
import spark.components.mediaClasses.VolumeBar;
private var socket:Socket;
protected function application1_applicationCompleteHandler(event:FlexEvent):void
{
socket = new Socket("127.0.0.1", 10000);
socket.addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
socket.addEventListener(Event.CONNECT, connectHandler);
}
protected function btnSend_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
socket.writeUTFBytes(inputMessage.text + "\n");
socket.flush();
}
private function connectHandler(event:Event):void
{
trace("접속완료!");
hbox.visible = true;
}
private function socketDataHandler(event:ProgressEvent):void
{
var message:String = socket.readUTFBytes(socket.bytesAvailable);
trace("수신메세지 : " + message);
textResult.text = message;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:layout>
<s:VerticalLayout />
</s:layout>
<mx:HBox id="hbox" visible="false" width="100%" horizontalAlign="center">
<s:TextInput id="inputMessage" />
<s:Button id="btnSend" label="송신하기" click="btnSend_clickHandler(event)"/>
</mx:HBox>
<mx:Text id="textResult" width="100%" textAlign="center"/>
</s:Application>
Socket만들어서 10000포트로 연결합니다. 그러면 도메인이 다르게 되면 swf가 843포트로 "<policy-file-request/>"를 날려서 정책파일을 달라고하는데, 서버에서 만들어놓은 PolicyFileServer가 정책파일을 내려주면 받게되면 10000포트로 다시 연결해 연결을 시작하게 됩니다.
몬가 별거 없는데 장황하게 설명해놨네.
그리고, 이런식으로 PolicyFileServer를 서버어플에 통합하면 안되겠죠? 나중에 서버어플이 늘어난다면 계속 새로 추가해야하니, PolicyfileServer를 따로 만들어서 띄워놓으면 되겠죠?^^
일단, 여러 폴리시서버 예제는 구글링하면 많이 나와요~
여기 아래주소는 Java, PHP, C#, VB.NET, Python 등등 예제가 있어요.
http://code.google.com/p/assql/wiki/SecurityInformation
이건 c로 만든거!
http://panzergruppe.hp.infoseek.co.jp/fspfd.html
No Comment
일단 기록용으로-_-
public class Test extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
StringBuilder content = new StringBuilder();
try
{
String data = "userid=???&password=???";
URL url = new URL("https://url~~");
HttpURLConnection http = null;
if (url.getProtocol().toLowerCase().equals("https")) {
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
https.setHostnameVerifier(DO_NOT_VERIFY);
http = https;
} else {
http = (HttpURLConnection) url.openConnection();
}
http.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(http.getOutputStream());
wr.write(data);
wr.flush();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(http.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null)
{
content.append(line + "\n");
}
Log.i("content", content.toString());
wr.close();
bufferedReader.close();
}
catch(Exception e)
{
}
}
private static void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[] chain,
String authType)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain,
String authType)
throws java.security.cert.CertificateException {
// TODO Auto-generated method stub
}
} };
// Install the all-trusting trust manager
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection
.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
}
기록용으로 기록합니다-_-
아래 예제는.....티월드사이트의 무료사용량 조회 예제입니다-_-
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
public class Main {
/**
* @param args
*/
public static void main(String[] args) throws Exception{
HttpClient httpclient = new DefaultHttpClient();
String id = "t월드 아이디";
String pw = "비밀번호";
List<NameValuePair> qparams = new ArrayList<NameValuePair>();
qparams.add(new BasicNameValuePair("URL", "http://www.tworld.co.kr/loginservlet.do?returnURL=http%3A%2F%2Fwww.tworld.co.kr&kind=&popup=&cmd=&reload=&ID=" + id));
qparams.add(new BasicNameValuePair("ID", id));
qparams.add(new BasicNameValuePair("PASSWORD", pw));
qparams.add(new BasicNameValuePair("SERVERIP", "203.236.20.129"));
qparams.add(new BasicNameValuePair("X", "0"));
qparams.add(new BasicNameValuePair("Y", "0"));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(qparams, "UTF-8");
HttpPost httpPost = new HttpPost("http://nicasams.sktelecom.com:2040/icas/fc/LogOnSV");
httpPost.setEntity(entity);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String responseBody = "";
HttpResponse response = httpclient.execute(httpPost);
Header[] headers = response.getAllHeaders();
httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet();
if (headers.length > 1){
String url = headers[1].getValue();
System.out.println("url = " + url);
httpGet.setURI(new URI(url));
responseBody = httpclient.execute(httpGet, responseHandler);
System.out.println(responseBody);
}
httpGet.setURI(new URI("http://www.tworld.co.kr/normal.do?serviceId=S_BILL0070&viewId=V_CENT0261"));
responseBody = httpclient.execute(httpGet, responseHandler);
System.out.println("result = " + responseBody);
}
}
Java는 원래 다 그런 것인가....-_- 자바는 셋팅이 반이라는 말이 있는데, 이놈은 셋팅도 다시 해야하고, 사실 언어만 java지 안드로이드에 맞는 class와 구조를 다시 배워야하는 거라 자바를 좀 안다는 사람이나 자바를 전혀 모르는 사람이나 뭐 차이가 없습니다.
그리고, AVD(Android Vritual Device)라고 해서 가상 에뮬레이터에서 실행할 수 있고, 모토로이에서도 실행할 수 있는데, 일단 가상에뮬레이터는 좀 느립니다. EditText부분에 글을 쓸 때 반응속도가 너무 느려서 짜증이 밀려내려옵니다.
그래서 개발할 때에는 모토로이에서 하고, 테스트할 때에는 에뮬레이터에서 하는 게 정신 건강에 좀 좋을 듯 합니다. 모토로이에서 개발하고 다른 해상도가 틀린 디바이스 때문에 에뮬레이터에서 테스트해줘야겠죠^^
사실 모토로이에서 디버깅하고 개발하는 게 의외로 편하고 잘 되어있습니다. 정말 신기했습니다^^
일단 셋팅에 대한 많은 글들이 있는데요.
http://john.tobe30.com/tc/292
http://blog.naver.com/ksewookk/100099954283
http://ihoney.pe.kr/entry/01-Android-%EC%84%A4%EC%B9%98%EB%B0%A9%EB%B2%95-%EB%B0%8F-%EC%98%88%EC%A0%9C-%EC%8B%A4%ED%96%89
여기 글들을 참조해서 셋팅을 하세요~
아....모토로이는 버전이 2.0.1이 탑재되어있습니다. 참고하세요~
그럼 프로젝트를 만들고 디버깅을 해봅시다.
1. 프로젝트 생성
Android Project -> Project name에는 TestDebug, Build Target은 2.0.1, Application name은 디버깅테스트, package name은 com.mudchobo.testdebug, Create Activity는 TestDebug 후 Finish.
EditText에 이름을 쓰고, Button을 누르면 Hello, 이름 이라는 어플을 만들겁니다.
main.xml파일 수정
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText
android:hint="이름입력하세요"
android:id="@+id/etHello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textShortMessage"
/>
<Button
android:text="sayHello"
android:id="@+id/btnHello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/tvHello"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>
AndroidManifest.xml파일을 선택하고, Application탭을 선택하게 되면 Application Attributes에 Debuggable옵션이 있는데 이걸 true로 줘야합니다.
그럼 src -> com.mudchobo.testdebug패키지에 있는 TestDebug.java파일을 수정해봅시다.
package com.mudchobo.testdebug;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class TestDebug extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 버튼에 이벤트 추가
Button btnHello = (Button) findViewById(R.id.btnHello);
// 버튼클릭 시 리스너추가
btnHello.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// edittext값 가져오기
EditText etHello = (EditText) findViewById(R.id.etHello);
String sayHello = etHello.getText().toString();
// 값을 textview에 셋팅
TextView tvHello = (TextView) findViewById(R.id.tvHello);
tvHello.setText(sayHello + ", Hello!");
}
});
}
}그냥 버튼에 이벤트 추가하고, 버튼클릭 시 값가져와서 값을 셋팅하는 초간단 예제입니다.
2. 디버깅 포인트 잡기
EditText etHello~~ 부분에 Ctrl + Shift + B를 누르거나 맨 앞에 더블클릭해서 Break Point를 잡습니다.
그리고 TestDebug에 오른쪽버튼 누르고, Debug As -> Android Application을 선택하면 실행합니다.
AVD가 떠 있다면 떠 있는 곳으로 어플을 설치하며 실행하고, 없으면 직접 AVD를 실행해서 설치하여 실행합니다.
AVD를 미리 띄워놓고 하는 게 정신건강에 좋습니다. 안그러면 프로그램에 끝나는 순간 AVD도 같이 죽어버립니다-_- 나중에 다시 시작하려고 하면 또 AVD를 부팅하는 꼴이 되어버립니다-_-
암튼, 실행하고 EditText에 말을 입력하고 버튼을 누르면 breakpoint에서 멈출겁니다. 이제 디버깅 하면 됩니다.
3. 모토로이에서 디버깅
이제 모토로이에서 실행해봅시다.
일단 모토로이를 디버깅 가능하게 셋팅해야 합니다.
설정 -> 응용프로그램 -> 개발 -> USB 디버깅 체크하셔서 활성화 시켜주세요.
모토로이를 usb에 연결합니다.
그리고 위와 같이 디버깅을 시작합니다. 그러면 이창이 뜰꺼에요.
4. 테스트하고 남은 어플 지우기
그리고 테스트하고 남은 어플은 여기서 지우시면 됩니다.
설정 -> 응용프로그램 -> 응용프로그램 관리 -> 디버그테스트(아까 프로젝트 만들 때 Application Name을 찾으면 됩니다. abcd가나다 순이니 참고하시길^^)선택 후 제거하면 됩니다.
PS. 뭐 별것도 없는 글을 장문으로 써버렸네-_-
[Java] Google App Engine에서 Spring3 + JPA 사용한 소녀시대 예제
Posted in 자바(Java) // Posted at 2010/01/31 20:50암튼, "구글 앱 엔진"에서는 JPA를 지원합니다. 하지만, 이상하게도 잘 안됩니다-_- 굉장히 제한적으로 이것저것 막아둔 것 같습니다. 사실 구글 앱 엔진에서는 DataBase를 BigTable인지 뭐시기인지 그걸 사용하고, 직접적으로 접근을 못하기 때문에(전부 프로그래밍 또는 관리페이지(관리페이지도 매우 제한적인-_-)에서만 관리 가능), 이걸 이용하는 API에서도 엄청나게 뭔가 막아둔 것 같습니다.
뭐 좀 해보려고 하면 에러를 내뱉습니다. 검색해보면 구글앱엔진에서만 나는 에러입니다-_- 사실 아직 구글앱엔진이 프리뷰버전이기에 뭐라 따지지도 못하는 게 사실입니다^^ 정식버전(언제나오려나....Beta딱지 떼는데 10년넘게 걸리겠지-_-)나오면 매우 안정화가 되지 않을까 싶습니다^^
암튼, Spring3 + JPA의 조합으로 앱엔진에 올리는 건 성공했는데, 사실 스프링에서 제공하는 TransactionManager를 사용했어야 했는데, JPATemplate으로 뭔가 처리를 하면 잘 안되더군요-_- 일단 가져오고, persist하고, 이런건 잘 되는데, 왜 삭제가 안될까요-_- 삭제가 안되서 그냥JPATemplate빼고 했습니다-_-
JPATemplate사용해서 성공하신 분 트랙백좀 ㅠㅠ
0. 환경
Eclipse 3.5 + Google AppEngine Plugin + Spring 3.0.0
일단 스프링3다운로드 - http://www.springsource.org/download
1. 프로젝트 생성
New Project -> Google에 있는 Web Application Project 선택.
Project Name은 SosiSchedule. package는 com.mudchobo.
Use Google Web Toolkit은 체크해제. 사용안할꺼라....(이것도 언제한번 공부해야하는데-_-)
Finish.
2. 라이브러리 복사 및 build path추가
spring3에서는 spring.jar가 산산조각 났어요. 필요한 것만 넣으면 되는 듯.
일단 제가 사용한 것은....
org.springframework.asm-3.0.0.RELEASE.jar
org.springframework.beans-3.0.0.RELEASE.jar
org.springframework.context-3.0.0.RELEASE.jar
org.springframework.core-3.0.0.RELEASE.jar
org.springframework.expression-3.0.0.RELEASE.jar
org.springframework.orm-3.0.0.RELEASE.jar
org.springframework.web.servlet-3.0.0.RELEASE.jar
org.springframework.web-3.0.0.RELEASE.jar
그리고, jstl을 사용할 것이기에....
jstl.jar와 standard.jar
※이번버전에서는 lib폴더가 없습니다-_- 어디서 찾아야하는 거지-_- 암튼 그래서 2.5.6버전에서 가져왔습니다^^
앱엔진에서는 lib폴더 복사로 libpath가 잡히지 않네요. 그래서 각각 다 추가해줘야한다는...-_-
일단 war/WEB-INF/lib폴더에 복사 후에 복사한 파일 선택 후 오른쪽버튼 후, Build Path -> Add to Build Path 선택하면 됩니다^^
3. web.xml파일 수정
web.xml
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/sosischedule/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>일단 sosischedule/*요청은 spring이 받습니다.
4. dispacher-servlet.xml파일과 persistence.xml파일 생성
war/WEB-INF/폴더에 생성
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.mudchobo" />
<bean id="entityManager"
factory-bean="EMF"
factory-method="get" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
</beans>src/META-INF/ 폴더에 생성
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="transactions-optional">
<provider>org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider</provider>
<properties>
<property name="datanucleus.NontransactionalRead" value="true"/>
<property name="datanucleus.NontransactionalWrite" value="true"/>
<property name="datanucleus.ConnectionURL" value="appengine"/>
</properties>
</persistence-unit>
</persistence>5. EMF클래스 생성.
이제 jpa접근할 수 있는 EntityManagerFactory클래스(EMF)를 생성해봅시다.
com.mudchobo.sosi.sosischedule.dao.EMF.java
package com.mudchobo.sosischedule.dao;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.springframework.stereotype.Component;
@Component
public final class EMF {
private static final EntityManagerFactory emfInstance =
Persistence.createEntityManagerFactory("transactions-optional");
private EMF() {}
public EntityManager get() {
return emfInstance.createEntityManager();
}
}6. Entity클래스 생성
일단 Sosi와 Schedule이라는 Entity를 생성할 건데요. 둘의 관계는 1:N관계입니다.
com.mudchobo.sosischedule.entity.Sosi.java
package com.mudchobo.sosischedule.entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import com.google.appengine.api.datastore.Key;
@Entity
public class Sosi implements Serializable {
private static final long serialVersionUID = 5448408922872112420L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Key key;
private String sosiName;
@OneToMany(mappedBy="sosi", cascade=CascadeType.ALL)
private List<Schedule> scheduleList = new ArrayList<Schedule>();
public Key getKey() {
return key;
}
public void setKey(Key key) {
this.key = key;
}
public List<Schedule> getScheduleList() {
return scheduleList;
}
public void setScheduleList(List<Schedule> scheduleList) {
this.scheduleList = scheduleList;
}
public String getSosiName() {
return sosiName;
}
public void setSosiName(String sosiName) {
this.sosiName = sosiName;
}
public Sosi() {
}
public Sosi(Key key, String sosiName) {
super();
this.key = key;
this.sosiName = sosiName;
}
}com.mudchobo.sosischedule.entity.Schedule.java
package com.mudchobo.sosischedule.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
@Entity
public class Schedule implements Serializable{
private static final long serialVersionUID = -8676837674549793653L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String program;
@ManyToOne(fetch=FetchType.LAZY)
private Sosi sosi;
public Sosi getSosi() {
return sosi;
}
public void setSosi(Sosi sosi) {
this.sosi = sosi;
}
public Key getKey() {
return key;
}
public void setKey(Key key) {
this.key = key;
}
public String getKeyString() {
return KeyFactory.keyToString(key);
}
public String getProgram() {
return program;
}
public void setProgram(String program) {
this.program = program;
}
public Schedule() {
}
public Schedule(String program, Sosi sosi) {
this.program = program;
this.sosi = sosi;
}
}일단 App Engine용 JPA에서는 ID 타입이 Long이면 관계형태를 사용할 수 없더라구요. 그래서 앱엔진에서 제공하는 Key타입이 있는데, 이걸 이용해야합니다.
7. Dao만들기
com.mudchobo.sosisochedule.SosiDao.java
package com.mudchobo.sosischedule.dao;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.google.appengine.api.datastore.KeyFactory;
import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;
@Repository
public class SosiDao {
private EntityManager em;
@Autowired
public void setEntityManager(EntityManager em) {
this.em = em;
// 소시데이터 추가
addSosi(new Long(1), "효연");
addSosi(new Long(2), "윤아");
addSosi(new Long(3), "수영");
addSosi(new Long(4), "유리");
addSosi(new Long(5), "태연");
addSosi(new Long(6), "제시카");
addSosi(new Long(7), "티파니");
addSosi(new Long(8), "써니");
addSosi(new Long(9), "서현");
}
public void addSosi(Long id, String sosiName) {
em.getTransaction().begin();
em.persist(new Sosi(KeyFactory.createKey(Sosi.class.getSimpleName(), id), sosiName));
em.getTransaction().commit();
}
@SuppressWarnings("unchecked")
public List<Sosi> getSosiList() {
return em.createQuery("select s from Sosi s").getResultList();
}
public Sosi getSosi(Long sosiId) {
return em.find(Sosi.class, sosiId);
}
@SuppressWarnings("unchecked")
public List<Schedule> getScheduleList(final Long sosiId) {
Query q = em.createQuery("select s.scheduleList from Sosi s where s.key = :key");
q.setParameter("key", KeyFactory.createKey(Sosi.class.getSimpleName(), sosiId));
return (List<Schedule>) q.getSingleResult();
}
public void addSchedule(Long sosiId, String program) {
em.getTransaction().begin();
Sosi sosi = em.find(Sosi.class, sosiId);
sosi.getScheduleList().add(new Schedule(program, sosi));
em.getTransaction().commit();
}
public void deleteSchedule(String scheduleKey) {
em.getTransaction().begin();
Schedule schedule = em.find(Schedule.class, scheduleKey);
em.remove(schedule);
em.getTransaction().commit();
}
}EntityManager받을 때 디폴트로 데이터를 넣어줘야 합니다(아까 위에서 말했듯이 프로그래밍적으로만 테이블을 생성할 수 있어서 이런 형태로 데이터를 넣어줘야합니다ㅠㅠ)
일단 실행해보고 데이터가 잘 생성되었는지 보려면 아래와 같은 주소로 접속해보면 됩니다.
http://localhost:8888/_ah/admin
일단 보고 삭제까지는 되는데, 테이블 생성같은 건 안되더라구요. 그리고 여기서 보여지는데에는 한글이 깨지는데 나중에 출력해보면 잘 나오니 걱정마시길-_-
com.mudchobo.sosischedule.service.SosiService.java

package com.mudchobo.sosischedule.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.mudchobo.sosischedule.dao.SosiDao;
import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;
@Service
public class SosiService {
@Autowired
private SosiDao sosiDao;
public List<Sosi> getSosiList()
{
return sosiDao.getSosiList();
}
public Sosi getSosi(Long sosiId) {
return sosiDao.getSosi(sosiId);
}
public List<Schedule> getScheduleList(Long sosiId) {
return sosiDao.getScheduleList(sosiId);
}
public void deleteSchedule(String scheduleKey) {
sosiDao.deleteSchedule(scheduleKey);
}
public void addSchedule(Long sosiId, String program) {
sosiDao.addSchedule(sosiId, program);
}
}Service에서 하는 역할은 뭐 없네요-_-
9. Controller생성
스프링3.0에서 새로 추가된 기능인 REST기능입니다.
com.mudchobo.sosischedule.controller.SosiController.java
package com.mudchobo.sosischedule.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.mudchobo.sosischedule.entity.Schedule;
import com.mudchobo.sosischedule.entity.Sosi;
import com.mudchobo.sosischedule.service.SosiService;
@Controller
public class SosiController {
private static String PREFIX = "/sosischedule";
@Autowired
private SosiService sosiService;
@RequestMapping(value="/", method=RequestMethod.GET)
public String index(Model model) {
List<Sosi> sosiList = sosiService.getSosiList();
model.addAttribute("sosiList", sosiList);
return "index";
}
@RequestMapping(value="/schedule/{sosiId}", method=RequestMethod.GET)
public String getSchedule(
@PathVariable("sosiId") Long sosiId,
Model model) {
Sosi sosi = sosiService.getSosi(sosiId);
List<Schedule> scheduleList = sosiService.getScheduleList(sosiId);
model.addAttribute("scheduleList", scheduleList)
.addAttribute("sosi", sosi);
return "sosi";
}
@RequestMapping(value="/schedule/{sosiId}/add", method=RequestMethod.POST)
public String addSchedule(
@PathVariable("sosiId") Long sosiId,
@RequestParam("program") String program,
Model model
) {
sosiService.addSchedule(sosiId, program);
return "redirect:" + PREFIX + "/schedule/" + sosiId;
}
@RequestMapping(value="/schedule/{sosiId}/{scheduleKey}", method=RequestMethod.GET)
public String removeSchedule(
@PathVariable("sosiId") Long sosiId,
@PathVariable("scheduleKey") String scheduleKey,
Model model) {
sosiService.deleteSchedule(scheduleKey);
return "redirect:" + PREFIX + "/schedule/" + sosiId;
}
}10. View jsp파일 생성
소시 리스트를 보여주는 index파일 입니다.
war/WEB-INF/jsp/index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>소녀시대 스케줄</title>
</head>
<body>
<div>
스케줄 확인하기
<ul>
<c:forEach var="sosi" items="${sosiList}">
<li><a href="/sosischedule/schedule/${sosi.key.id}">${sosi.key.id}. ${sosi.sosiName}</a></li>
</c:forEach>
</ul>
</div>
</body>
</html>해당 소시의 스케줄을 보여주는 스케줄 파일입니다.
war/WEB-INF/jsp/sosi.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="UTF-8"%>
<%@ page isELIgnored="false" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>소녀시대 스케줄</title>
</head>
<body>
<div>
스케줄 확인하기
<ul>
<c:forEach var="sosi" items="${sosiList}">
<li><a href="/sosischedule/schedule/${sosi.key.id}">${sosi.key.id}. ${sosi.sosiName}</a></li>
</c:forEach>
</ul>
</div>
</body>
</html>리다이렉트를 위한 파일입니다. 기존 index.html파일 지우시고, index.jsp파일 생성
index.jsp
<% response.sendRedirect("/sosischedule/"); %>앱엔진에 올려보았습니다.
http://2.latest.mudchobosample.appspot.com/sosischedule/
잘 되는 것 같네요.
Tags App Engine,
google,
JAVA,
java persistence API,
jpa,
spring,
springframework,
구글,
구글 앱엔진,
소녀시대,
스프링,
스프링프레임워크,
자바,
태연짱
-
-
머드초보
안녕하세요~ 답변이 늦었네요 ㅠㅠ
근데, 파일 다 올린 것 같은데...
저도 만들어놓고 그냥 올린거다보니 ㅠㅠ
뭐가 빠진건가요? ㅠㅠ
-

