Sunday 27 July 2014

Debug Weblogic in Eclipse

Вообще то существует Oracle plugin для эклипса, но дело в том что мне он весь ни к чему. А весь он жутко тормозит IDE. Так как все проекты в основном подкреплены скриптами, ведь в любом случае надеяться на IDE как на инструмент который скомпилирует, запакует и развернет проект - нельзя, к сожалению опыт показывает что айдиишки ломаются по самым разным причинам, из за какого нибудь плагина который и нанесет ему урон). Надежную структуру проекту придает скрипт, он дает ему силу, возможность оказывать действие и пользу, как каркас плотно связывающий бесполезные доски корабля. Но если без философии, то вот как можно установить debugging и запуск Weblogic сервера из эклипса.

Запуск
1) Зайдите в созданную папку bin доменa и откройте файл startWebLogic.cmd - это на Windows. Вот этот файл:

C:\Oracle\Middleware\Oracle_Home\user_projects\domains\technokon\bin\startWebLogic.cmd

2) Добавьте туда вот этот кусок:

@REM set debug options
set JAVA_OPTIONS=%JAVA_OPTIONS% -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

Suspend=n означает что Debuger не будет ждать запуска listener с порта 8000.

Вот файл целиком:

@ECHO OFF

@REM WARNING: This file is created by the Configuration Wizard.
@REM Any changes to this script may be lost when adding extensions to this configuration.

SETLOCAL

@REM --- Start Functions ---

GOTO :ENDFUNCTIONS

:stopAll
 @REM We separate the stop commands into a function so we are able to use the trap command in Unix (calling a function) to stop these services
 if NOT "X%ALREADY_STOPPED%"=="X" (
  GOTO :EOF
 )
 @REM STOP DERBY (only if we started it)
 if "%DERBY_FLAG%"=="true" (
  echo Stopping Derby server...
  call "%WL_HOME%\common\derby\bin\stopNetworkServer.cmd"  >"%DOMAIN_HOME%\derbyShutdown.log" 2>&1 

  echo Derby server stopped.
 )

 set ALREADY_STOPPED=true
GOTO :EOF

:classCaching
 echo Class caching enabled...
 set JAVA_OPTIONS=%JAVA_OPTIONS% -Dlaunch.main.class=%SERVER_CLASS% -Dlaunch.class.path="%CLASSPATH%" -Dlaunch.complete=weblogic.store.internal.LockManagerImpl -cp %WL_HOME%\server\lib\pcl2.jar
 set SERVER_CLASS=com.oracle.classloader.launch.Launcher
GOTO :EOF


:ENDFUNCTIONS

@REM --- End Functions ---

@REM *************************************************************************
@REM This script is used to start WebLogic Server for this domain.
@REM 
@REM To create your own start script for your domain, you can initialize the
@REM environment by calling @USERDOMAINHOME\setDomainEnv.
@REM 
@REM setDomainEnv initializes or calls commEnv to initialize the following variables:
@REM 
@REM BEA_HOME       - The BEA home directory of your WebLogic installation.
@REM JAVA_HOME      - Location of the version of Java used to start WebLogic
@REM                  Server.
@REM JAVA_VENDOR    - Vendor of the JVM (i.e. BEA, HP, IBM, Sun, etc.)
@REM PATH           - JDK and WebLogic directories are added to system path.
@REM WEBLOGIC_CLASSPATH
@REM                - Classpath needed to start WebLogic Server.
@REM PATCH_CLASSPATH - Classpath used for patches
@REM PATCH_LIBPATH  - Library path used for patches
@REM PATCH_PATH     - Path used for patches
@REM WEBLOGIC_EXTENSION_DIRS - Extension dirs for WebLogic classpath patch
@REM JAVA_VM        - The java arg specifying the VM to run.  (i.e.
@REM                - server, -hotspot, etc.)
@REM USER_MEM_ARGS  - The variable to override the standard memory arguments
@REM                  passed to java.
@REM PRODUCTION_MODE - The variable that determines whether Weblogic Server is started in production mode.
@REM DERBY_HOME - Derby home directory.
@REM DERBY_CLASSPATH
@REM                - Classpath needed to start Derby.
@REM 
@REM Other variables used in this script include:
@REM SERVER_NAME    - Name of the weblogic server.
@REM JAVA_OPTIONS   - Java command-line options for running the server. (These
@REM                  will be tagged on to the end of the JAVA_VM and
@REM                  MEM_ARGS)
@REM SAVE_MEMORY    - Put server in a mode where it is frugal with memory, potentially sacrificing performance to do so.
@REM CLASS_CACHE    - Enable class caching of system classpath.
@REM PROXY_SETTINGS - These are tagged on to the end of the JAVA_OPTIONS. This variable is deprecated and should not
@REM                  be used. Instead use JAVA_OPTIONS
@REM 
@REM For additional information, refer to "Administering Server Startup and Shutdown for Oracle WebLogic Server"
@REM *************************************************************************

@REM Call setDomainEnv here.

set DOMAIN_HOME=C:\Oracle\Middleware\Oracle_Home\user_projects\domains\technokon
for %%i in ("%DOMAIN_HOME%") do set DOMAIN_HOME=%%~fsi

call "%DOMAIN_HOME%\bin\setDomainEnv.cmd" %*

set SAVE_JAVA_OPTIONS=%JAVA_OPTIONS%

set SAVE_CLASSPATH=%CLASSPATH%

@REM Start Derby

set DERBY_DEBUG_LEVEL=0

if "%DERBY_FLAG%"=="true" (
 call "%WL_HOME%\common\derby\bin\startNetworkServer.cmd"  >"%DOMAIN_HOME%\derby.log" 2>&1 

)

set JAVA_OPTIONS=%SAVE_JAVA_OPTIONS%

set SAVE_JAVA_OPTIONS=

set CLASSPATH=%SAVE_CLASSPATH%

set SAVE_CLASSPATH=

if "%PRODUCTION_MODE%"=="true" (
 set WLS_DISPLAY_MODE=Production
) else (
 set WLS_DISPLAY_MODE=Development
)

if NOT "%WLS_USER%"=="" (
 set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.management.username=%WLS_USER%
)

if NOT "%WLS_PW%"=="" (
 set JAVA_OPTIONS=%JAVA_OPTIONS% -Dweblogic.management.password=%WLS_PW%
)

if NOT "%MEDREC_WEBLOGIC_CLASSPATH%"=="" (
 if NOT "%CLASSPATH%"=="" (
  set CLASSPATH=%CLASSPATH%;%MEDREC_WEBLOGIC_CLASSPATH%
 ) else (
  set CLASSPATH=%MEDREC_WEBLOGIC_CLASSPATH%
 )
)

@REM set debug options
set JAVA_OPTIONS=%JAVA_OPTIONS% -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n

echo .

echo .

echo JAVA Memory arguments: %MEM_ARGS%

echo .

echo CLASSPATH=%CLASSPATH%

echo .

echo PATH=%PATH%

echo .

echo ***************************************************

echo *  To start WebLogic Server, use a username and   *

echo *  password assigned to an admin-level user.  For *

echo *  server administration, use the WebLogic Server *

echo *  console at http:\\hostname:port\console        *

echo ***************************************************

@REM SAVE MEMORY

if exist %WL_HOME%\server\bin\saveMemory (
 call "%WL_HOME%\server\bin\saveMemory.cmd"
)

@REM CLASS CACHING

if "%CLASS_CACHE%"=="true" (
 CALL :classCaching
)

@REM START WEBLOGIC

echo starting weblogic with Java version:

%JAVA_HOME%\bin\java %JAVA_VM% -version

if "%WLS_REDIRECT_LOG%"=="" (
 echo Starting WLS with line:
 echo %JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% -Dweblogic.Name=%SERVER_NAME% -Djava.security.policy=%WLS_POLICY_FILE% %JAVA_OPTIONS% %PROXY_SETTINGS% %SERVER_CLASS%
 %JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% -Dweblogic.Name=%SERVER_NAME% -Djava.security.policy=%WLS_POLICY_FILE% %JAVA_OPTIONS% %PROXY_SETTINGS% %SERVER_CLASS%
) else (
 echo Redirecting output from WLS window to %WLS_REDIRECT_LOG%
 %JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% -Dweblogic.Name=%SERVER_NAME% -Djava.security.policy=%WLS_POLICY_FILE% %JAVA_OPTIONS% %PROXY_SETTINGS% %SERVER_CLASS%  >"%WLS_REDIRECT_LOG%" 2>&1 
)

CALL :stopAll

popd

@REM Exit this script only if we have been told to exit.

if "%doExitFlag%"=="true" (
 exit
)



ENDLOCAL

Файл этот используется для запуска сервера. Сохраните файл как нибудь в той-же папке, я свой назвал - startWebLogicInDebug.cmd

3) В эклипсе добавьте External tool configurations, для это-го идите в:

Run -> External Tools -> External Tools Configurations... 

Там создайте 2 конфигурации, для запуска и для стопа:





В Location укажите на файлы для запуска, это вот новый подготовленный файл, и файл для стопа, он в той-же директории.


 Debuggin
В эклипсе зайдите в:

Run -> Debug Configurations... 

А там:


Вот и все. Теперь в начале запускаем сервер, потом Debug. В Debug, в табе Source, вы можете указать проекты по которым желаете пройтись во время работы сервера.  

Sunday 20 July 2014

Учебное пособие по maven

Maven - это современный расширенный build tool.

Загрузите и установите Maven. На той же странице, вы найдете инструкции по установке, там где написано:

Installation Instructions



Теперь попробуем с ним поработать. Прошу учесть что последовательность действий я беру с книги Apache Maven 3 Cookbook. Итак, приступим-с. В директории, где у нас должны помещаться проекты, пишем:

PS C:\Users\Administrator> mvn archetype:generate
[INFO] Scanning for projects...
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.
pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.p
om (4 KB at 8.1 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/22/maven-plugins-22.pom (13 KB at
 78.1 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/maven-parent/21/maven-parent-21.pom (26 KB at 102.1 KB/
sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/apache/10/apache-10.pom (15 KB at 81.2 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.
jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-clean-plugin/2.5/maven-clean-plugin-2.5.j
ar (25 KB at 79.9 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-
2.4.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-install-plugin/2.4/maven-install-plugin-2
.4.pom (7 KB at 43.3 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom (9 KB at
60.7 KB/sec)
...

Затем появится вопрос о выборе номера

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 439:

Выбираем предлагаемый (нажимаем ввод)

Затем появляется вопрос о версии archetype, опять-же выбираем предлагаемый.

Choose org.apache.maven.archetypes:maven-archetype-quickstart version:
1: 1.0-alpha-1
2: 1.0-alpha-2
3: 1.0-alpha-3
4: 1.0-alpha-4
5: 1.0
6: 1.1
Choose a number: 6:

 Затем идет запрос о groupId, пишем:

pe-quickstart-1.1.pom (2 KB at 10.1 KB/sec)
Define value for property 'groupId': : com.test.project

Затем artifactId, пишем:

Define value for property 'artifactId': : myProject

Затем идет запрос о версии проекта и подтверждении пакеджа. Нажимаем везде ввод и вод к чему в конце концов приводит процесс постройки:

Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  com.test.project: :
Confirm properties configuration:
groupId: com.test.project
artifactId: myProject
version: 1.0-SNAPSHOT
package: com.test.project
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: C:\Users\Administrator
[INFO] Parameter: package, Value: com.test.project
[INFO] Parameter: groupId, Value: com.test.project
[INFO] Parameter: artifactId, Value: myProject
[INFO] Parameter: packageName, Value: com.test.project
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: C:\Users\Administrator\myProject
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 29:20 min
[INFO] Finished at: 2014-07-20T13:27:06-04:00
[INFO] Final Memory: 10M/79M
[INFO] ------------------------------------------------------------------------
PS C:\Users\Administrator>

Вот что для нас сгенерировал maven:

PS C:\Users\Administrator> tree /F myProject
Folder PATH listing
Volume serial number is CC58-8E5C
C:\USERS\ADMINISTRATOR\MYPROJECT
   pom.xml

└───src
    ├───main
       └───java
           └───com
               └───test
                   └───project
                           App.java
    
    └───test
        └───java
            └───com
                └───test
                    └───project
                            AppTest.java

PS C:\Users\Administrator>

Как видите, пока не густо, но ведь надо хоть с чего-то начинать).

Теперь давайте скомпилируем наше "произведение искусства".

Перейдите к папке самого проекта, то есть там где находится pom.xml сгенерированный и запустите compile:

PS C:\Users\Administrator> cd myProject
PS C:\Users\Administrator\myProject> mvn compile
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building myProject 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plu
gin-2.6.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plug
in-2.6.pom (8 KB at 18.6 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plu
gin-2.6.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plug
in-2.6.jar (29 KB at 184.8 KB/sec)
...
Downloaded: http://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-compiler-manager/1.9.1/plexus-compiler-manage
r-1.9.1.jar (5 KB at 23.2 KB/sec)
Downloaded: http://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0/plexus-utils-3.0.jar (221 KB at 679
.3 KB/sec)
[INFO] Compiling 1 source file to C:\Users\Administrator\myProject\target\classes
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.259 s
[INFO] Finished at: 2014-07-20T14:04:54-04:00
[INFO] Final Memory: 12M/72M
[INFO] ------------------------------------------------------------------------

Как видите, опять пошли загрузки и в конце результат компиляции. Давайте опять посмотрим что у нас из этого вышло:

PS C:\Users\Administrator\myProject> tree /F
Folder PATH listing
Volume serial number is CC58-8E5C
C:.
   pom.xml

├───src
   ├───main
      └───java
          └───com
              └───test
                  └───project
                          App.java
   
   └───test
       └───java
           └───com
               └───test
                   └───project
                           AppTest.java

└───target
    └───classes
        └───com
            └───test
                └───project
                        App.class

Заметь-те что скомпилировался только класс App, AppTest - нет. Maven создан таким образом, что бы придерживаться идеологии, или даже стандарту TDD (Test Driven Development), таким образом прежде чем создать решение, нужно подготовить для него тест, и вот после этого появляется четкое выражение, то-есть оно как бы приобретает видимую структуру, как должно выглядеть само решение, или по крайне мере более четкое чем казалось изначально. Идем дальше и выполняем тест:

PS C:\Users\Administrator\myProject> mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building myProject 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/2.12.4/maven-surefire-pl
ugin-2.12.4.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/2.12.4/maven-surefire-plu
gin-2.12.4.pom (11 KB at 24.2 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire/2.12.4/surefire-2.12.4.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire/2.12.4/surefire-2.12.4.pom (14 KB at
103.6 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/2.12.4/maven-surefire-pl
ugin-2.12.4.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-surefire-plugin/2.12.4/maven-surefire-plu
gin-2.12.4.jar (30 KB at 128.2 KB/sec)
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ myProject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\Administrator\myProject\src\main\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) @ myProject ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ myProject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\Administrator\myProject\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) @ myProject ---
[INFO] Compiling 1 source file to C:\Users\Administrator\myProject\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ myProject ---
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-booter/2.12.4/surefire-booter-2.12.4
.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-booter/2.12.4/surefire-booter-2.12.4.
pom (3 KB at 24.1 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-api/2.12.4/surefire-api-2.12.4.pom
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-api/2.12.4/surefire-api-2.12.4.pom (3
 KB at 18.3 KB/sec)
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/maven-surefire-common/2.12.4/maven-surefire-c
ommon-2.12.4.pom
...
Downloading: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit3/2.12.4/surefire-junit3-2.12.4
.jar
Downloaded: http://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit3/2.12.4/surefire-junit3-2.12.4.
jar (26 KB at 167.6 KB/sec)

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.test.project.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.205 s
[INFO] Finished at: 2014-07-20T14:13:12-04:00
[INFO] Final Memory: 13M/72M
[INFO] ------------------------------------------------------------------------
PS C:\Users\Administrator\myProject>

Заметь-те, опять куча загрузок, компиляция тест класса и затем запуск с результатом. И вот что у нас вышло на сей раз:

PS C:\Users\Administrator\myProject> tree /F /A
Folder PATH listing
Volume serial number is CC58-8E5C
C:.
|   pom.xml
|
+---src
|   +---main
|   |   \---java
|   |       \---com
|   |           \---test
|   |               \---project
|   |                       App.java
|   |
|   \---test
|       \---java
|           \---com
|               \---test
|                   \---project
|                           AppTest.java
|
\---target
    +---classes
    |   \---com
    |       \---test
    |           \---project
    |                   App.class
    |
    +---surefire-reports
    |       com.test.project.AppTest.txt
    |       TEST-com.test.project.AppTest.xml
    |
    \---test-classes
        \---com
            \---test
                \---project
                        AppTest.class

PS C:\Users\Administrator\myProject>

 В принципе у нас готов шаблон по элементарному проекту.


Wednesday 16 July 2014

try-with-resources

Недавно узнал про новую фичу, название которой, try-with-resources. С новой (уже не такой уж) семеркой, появилась возможность не закрывать ресурсы в блоке try catch, как обычно это делается в finally. Это считается стандартной практикой - ресурсы которые мы одалживаем надо возвращать. В них входят соединения с БД, I/O, разные объекты из всевозможных пулов и которые ждут своего возвращения назад после своей эксплуатации. Ресурсы в основном возвращаются при вызове метода close (это далеко не значит что это единственный метод, который может вернуть заемщику ресурс).


conn.close();

После выхода семерки на сцену, появился Interface AutoCloseable, и практически все пользуемые ресурсы, по крайней мере те что состоят в в пакеджах java, javax, его имплементируют, так-же  имеются и и интерфейсы расширяющие этот. Таким образом все ресурсы выполняющие этот интерфейс могут быть задекларированы в try-with-resources.

Суть и ее отображение

Последующий фрагмент, или снипет является примером работы с каким-то ресурсом.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
  SomeResource sr = null;
  
  try {
   
    sr = new SomeResource();
   
    sr.output(data);
   
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if (sr != null)
     sr.close();
   } catch (Exception e) {
    e.printStackTrace();
   }
  }

На первой строке - декларация ресурса, затем уже в try на пятой мы придаем ему значение, на седьмой - используем. Затем идет catch, в основном все ресурсы декларируют выброс exception-а како-го либо типа - в данном случае это просто Exception. Затем в finally который обеспечивает нас выполнением при любом исходе работы ресурса (выполнился ли код в try или выдал Exception) мы по обязанности возвращаем ресурс - строка 14, проверив перед тем если такой есть веди при выбросе Exception ресурс мог так и не инициализироваться. Заметьте что закрытие ресурса тоже выдает Exception, то-же обычное дело.

Новый формат

Вот как выглядит то-же самое при помощи try-with-resoources.

1
2
3
4
5
  try (SomeResource sr = new SomeResource();){
   sr.output(data);
  } catch (Exception e) {
   e.printStackTrace();
  } 

Первое что бросается в глаза, по сравнению со стандартным выполнением, так это то что код намного лаконичней. Ресурс на первой строке декларируется в скобках. После выполнения try блока, JVM вызовет метод close() интерфейса AutoClosable, просто это не указывается. Самое интересное что finally все равно можно указать, но переменная "sr" там уже оказывается вне существования. Лаконичность кода достигается за счет того что нету finally блока в котором производится проверка и закрытия ресурса. Хотя если бы в finally блоке нужно было-бы делать другие операции, то разница бы так не ощущалась. Читаемость и восприятие старого выполнения все равно четче.

AutoClosable

Интерфейс советует реализаторам, бросать InterruptedException и не подавлять ее, так как ее передача сигнализирует о состоянии interrupted статуса потока, сигнал на который какой-то процесс спроектирован реагировать. Еще с выполнение интерфейса может не быть идемпотентным, а это значит что поведение close() может быть динамичным и каждый вызов может нести разные последствия, например повторный вызов может бросить Exception какой нибудь, это не запрещается.  

Сравнение при запуске

Вот весь код, который мы сей час запустим и сравним как вылетает Exception при вызове close().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.j8.test;

public class SomeResource implements AutoCloseable {

 @Override
 public void close() throws Exception {
  
  throw new Exception ("This is just a test.");
  
 }
 
 public void output(String data) throws Exception {
  System.out.println(data);
 }

}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.j8.test;

public class TestTryWithResources {

 public static void main(String[] args) {
  TestTryWithResources test = new TestTryWithResources();

  test.theOldWayOfDealingWithResources("Test");
  test.newTryWithResources("Test");
 }

 public void theOldWayOfDealingWithResources(String data) {

  SomeResource sr = null;

  try {
   sr = new SomeResource();

   sr.output(data);
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    if (sr != null)
     sr.close();
   } catch (Exception e) {
    e.printStackTrace();
   }
  }

 }

 public void newTryWithResources(String data) {

  try (SomeResource sr = new SomeResource();) {
   sr.output(data);
  } catch (Exception e) {
   e.printStackTrace();
  }

 }

}


Output:

Test
java.lang.Exception: This is just a test.
 at com.j8.test.SomeResource.close(SomeResource.java:8)
 at com.j8.test.TestTryWithResources.theOldWayOfDealingWithResources(TestTryWithResources.java:25)
 at com.j8.test.TestTryWithResources.main(TestTryWithResources.java:8)
Test
java.lang.Exception: This is just a test.
 at com.j8.test.SomeResource.close(SomeResource.java:8)
 at com.j8.test.TestTryWithResources.newTryWithResources(TestTryWithResources.java:37)
 at com.j8.test.TestTryWithResources.main(TestTryWithResources.java:9)

Заметь-те что stack trace вызова theOldWayOfDealingWithResources указывает на линию 25, как раз там где sr.close(); - то есть четко видно где вылетела Exception. А вот stack trace newTryWithResources указывает на 37-ю строку - там где Exception ловится, и прямого указателя, в коде класса TestTryWithResources нет, правда в stack trace указанно что именно метод close()послужил Exception-у. 

Сказать что код читать намного сложнее с использованием tr-with-resources нельзя, и места он занимает намного меньше и выглядит проще.  

Friday 4 July 2014

Java 8 и лямбда

Пока в производстве она наверно есть далеко не везде, да и в некоторых местах вообще так и не появится. По крайне мере, лучшие и много-обещающие надежды на нее не возлагают.
Так, ну есть и есть... "Писать не писать - ну можно расписать че та, а вообще ну сами понимаете..."

Тем не менее пренебрегать темой не стоит - тоже хлеб. Постараюсь указать на некоторые возможные на мой взгляд применения.

For loop

1
2
3
4
5
6
7
8
9
                // Обычный цикл
  for (Person p : people) {
   System.out.println(p);
  }
  
  // Тоже удобно
  people.forEach(System.out::println);
  people.forEach(p->System.out.println(p.getBirthPlace()));
  

Цикл фор легко заменить, чем то простеньким в одну строчку. На 8-ой строке, мы передаем функции println не весь объект а один из его атрибутов. На седьмой-же строчку передается весь объект.

::

Этот значок означает вызов метода, в данном случае статического и как параметр посылает подразумеваемую ссылку на объект. В общем-то разница между стандартной росписи цикла и новой не велика, но например во время тестинга удобно. Помимо forEach есть еще несколько удобных методов, например следующее убирает элементы из коллекции удовлетворяющие условие.


people.removeIf(p->p.getBirthPlace().contains("г.Москва"));

То есть это заменит

1
2
3
4
5
6
                for (Iterator<Person> it = people.iterator(); it.hasNext();) {
   Person p = it.next();
   if (p.getBirthPlace().contains("г.Москва")) {
    it.remove();
   }
  }

Сама ламбда


->

По левую сторону указываются параметры, по правую - реализация метода какого либо.


Predicate
Это что-то новенькое но довольно удобное. Взгляните на следующий код.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
 // Поиск в коллекции.
 public static Person findPerson(List<Person> people, Predicate<Person> p) {
  Person found = null;
  
  for (Person person : people) {
   if (p.test(person)) {
    found = person;
    break;
   }
  }
  
  return found;
 }

Допустим что в результате определенной проверки мы должны найти нежный нам элемент. Определенная проверка здесь ключевое слово, потому что у каждой проверки может быть разный критерий. Так вот что-бы не писать кучу разных методов, мы можем написать реализацию интерфейса при вызове. С помощью ламбды это можно сделать следующим образом.


1
2
3
4
5
  // Вариант с Predicate
  Person guyFromLeningrad = findPerson(people, 
    p -> p.getSex().equalsIgnoreCase("MALE") && p.getBirthPlace().contains("г.Ленинград"));
  
  System.out.println(guyFromLeningrad);

По правую сторону от -> тут мы реализуем метод test интерфейса Predicate, в котором как параметр принимается элемент из итерации.

Comparators
Вот пример с комператорами. Но тут дело в то что они как правило часто повторно используются, особенно при сортировки элементов для UI.

Стандартный вариант.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
 public static final Comparator<Person> PASSPORT_COMPARATOR = new Comparator<Person>() {
  @Override
  public int compare(Person a, Person b) {
   int date = a.getBirthDate().compareTo(b.getBirthDate());
   if (date == 0) {
    return a.getLastName().compareTo(b.getLastName()) == 0 ? 
      0 : a.getFirstName().compareTo(b.getFirstName());
   }
   return date;
  }
 };

и

1
2
  // Стандартный вариант
  Collections.sort(people, PASSPORT_COMPARATOR);

или


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
  // Ламбда-Комператор 
  Collections.sort(people, (a, b) -> {
   int date = a.getBirthDate().compareTo(b.getBirthDate());
   if (date == 0) {
    int last = a.getLastName().compareTo(b.getLastName());
    if (last == 0) {
     return a.getFirstName().compareTo(b.getFirstName());
    }
    return last;
   }
   return date;
  });

Тут все конечно зависит от среды. Если преобладают annonimous реализации, то можно попробовать, а так...? )